Project

General

Profile

Bug #9037

Two timers problem: only one fires

Added by Vladimir Tsichevski almost 2 years ago. Updated 5 months ago.

Status:
WIP
Priority:
Normal
Assignee:
Vladimir Tsichevski
Target version:
-
Start date:
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
production:
No
env_name:
topics:

8393.p Magnifier (1.63 KB) Vladimir Tsichevski, 08/07/2024 03:38 PM

8393.p.ext-hints (193 Bytes) Vladimir Tsichevski, 08/07/2024 03:42 PM


Related issues

Related to User Interface - Bug #5115: Decide and implement PSTimer optimization New

History

#1 Updated by Vladimir Tsichevski almost 2 years ago

When two PSTimer instances are defined to fire simultaneously, sometimes only one of them fires.

To reproduce run the 8393.p example several times. In most cases, both timers fire as expected, but sometimes only one of them fires.

This issue was first discovered in #8393.

#3 Updated by Vladimir Tsichevski almost 2 years ago

The problem is that the following code can be executed in both PSTimer threads, so, the context for ServerEvent.events is created twice, the first time for the first server event instance, and second time for the second, and the registration of the first server event get lost.

    ServerEvent$1.initialValue() line: 116    
    ServerEvent$1.initialValue() line: 1    
    ServerEvent$1(ContextLocal<T>).getImpl(boolean) line: 542    
    ServerEvent$1(ContextLocal<T>).get() line: 454    
    ServerEvent.<init>(long, String, Runnable, boolean) line: 189    
    ServerEvent.<init>(long, String, Runnable) line: 162    
    ComServer.emit(ComEvent, boolean) line: 204    
    PSTimer.fireEvent() line: 678    
    PSTimer.run() line: 633    
    AssociatedThread(ContextAwareThread).run() line: 136    
    AssociatedThread.run() line: 1    

#4 Updated by Vladimir Tsichevski almost 2 years ago

  • Status changed from New to WIP

#5 Updated by Vladimir Tsichevski almost 2 years ago

  • % Done changed from 0 to 100
  • Status changed from WIP to Review

Branch 8393b rev. 15373 fixes this issue. Please, review.

#6 Updated by Greg Shah almost 2 years ago

Code Review Task Branch 8393b Revision 15373

I don't think it is correct to synchronize on dependencies. That is arbitrary and inconsistent with the other synchronization in use. Should it be synchronized on contextStorage?

#7 Updated by Vladimir Tsichevski almost 2 years ago

Greg Shah wrote:

Code Review Task Branch 8393b Revision 15373

I don't think it is correct to synchronize on dependencies. That is arbitrary and inconsistent with the other synchronization in use. Should it be synchronized on contextStorage?

Indeed. we can use any object singleton for synchronization. Agreed, the dependencies object causes questions, since it is otherwise unrelated to the purpose. The only problem with dependencies is if we ever want to change this variable definition to something mutable, we will also need to modify its usage in the synchronization.

We cannot use contextStorage for this, since this variable is used at the client side only, and is always null at the server side.

To make code more readable, we can add one more variable specially for the synchronization:

private final static Object lock = new Object();

#8 Updated by Greg Shah almost 2 years ago

Yes, that makes sense. Should we switch all of the current synchronization to that same lock object?

#9 Updated by Vladimir Tsichevski almost 2 years ago

Greg Shah wrote:

Yes, that makes sense. Should we switch all of the current synchronization to that same lock object?

Yes, we can use this special lock object for all "static" synchronization places in ContextLocal, except for the cases when the contextStorage is used to synchronize the access to contextStorage only, which is perfectly OK.

Also, I think, the following code should by executed synchronized:

         if (contextStorage == null)
         {
            contextStorage = new HashMap();
         }

#10 Updated by Greg Shah almost 2 years ago

Also, I think, the following code should by executed synchronized:

Agreed.

#11 Updated by Vladimir Tsichevski almost 2 years ago

Vladimir Tsichevski wrote:

Greg Shah wrote:

Yes, that makes sense. Should we switch all of the current synchronization to that same lock object?

Yes, we can use this special lock object for all "static" synchronization places in ContextLocal, except for the cases when the contextStorage is used to synchronize the access to contextStorage only, which is perfectly OK.

The access to contextStorage is intermixed with the access to other synchronized data, so you are right, to be sure, we better use one lock object for all statically synchronized sections.

#12 Updated by Vladimir Tsichevski almost 2 years ago

  • % Done changed from 100 to 90
  • Status changed from Review to WIP

I retested the issue once again: it appears sometimes still (but rarely), despite the problem with ServerEvent initialization registry is now fixed, and the registry is not overwritten.

#13 Updated by Vladimir Tsichevski almost 2 years ago

Vladimir Tsichevski wrote:

I retested the issue once again: it appears sometimes still (but rarely), despite the problem with ServerEvent initialization registry is now fixed, and the registry is not overwritten.

The problem is that the whole branch must be executed synchronized:

      else if (mode == MODE_SERVER)
      {
         ...
      }

Otherwise the following call can be executed more than one time per client:

    at com.goldencode.p2j.ui.client.event.ServerEvent$1.initialValue(ServerEvent.java:117)
    at com.goldencode.p2j.ui.client.event.ServerEvent$1.initialValue(ServerEvent.java:1)
    at com.goldencode.p2j.security.ContextLocal.getImpl(ContextLocal.java:545)
    at com.goldencode.p2j.security.ContextLocal.get(ContextLocal.java:455)
    at com.goldencode.p2j.ui.client.event.ServerEvent.<init>(ServerEvent.java:192)
    at com.goldencode.p2j.ui.client.event.ServerEvent.<init>(ServerEvent.java:163)
    at com.goldencode.p2j.comauto.ComServer.emit(ComServer.java:204)
    at com.goldencode.p2j.extension.PSTimer.fireEvent(PSTimer.java:678)
    at com.goldencode.p2j.extension.PSTimer.run(PSTimer.java:633)
    at com.goldencode.p2j.security.ContextAwareThread.run(ContextAwareThread.java:136)
    at com.goldencode.p2j.security.AssociatedThread.run(AssociatedThread.java:1)

I checked this by setting a conditional breakpoint in initValue, which dumps current thread stack trace.

#14 Updated by Vladimir Tsichevski almost 2 years ago

  • % Done changed from 90 to 100
  • Status changed from WIP to Review

The 8393b rev. 15374 contains the change for #9037-7 and the fix for the #9037-12 issue. Please, review.

#15 Updated by Vladimir Tsichevski almost 2 years ago

  • Assignee set to Vladimir Tsichevski

#16 Updated by Greg Shah almost 2 years ago

Code Review Task Branch 8393b Revision 15374

In checkCaller(), the final int prefixLength = "com.goldencode.p2j.".length(); should be a static final data member so that it is not recalculated multiple times.

Otherwise the change is fine. It needs heavy regression testing to ensure that there are no deadlocks. ChUI regression testing and unit testing with multiple applications will be needed.

Constantin: Please review.

#17 Updated by Vladimir Tsichevski almost 2 years ago

Greg Shah wrote:

Code Review Task Branch 8393b Revision 15374

In checkCaller(), the final int prefixLength = "com.goldencode.p2j.".length(); should be a static final data member so that it is not recalculated multiple times.

Done. After this fixed and the branch rebased, the new revno is 15389.

#18 Updated by Greg Shah almost 2 years ago

Constantin: Please review.

#20 Updated by Constantin Asofiei almost 2 years ago

What branch exactly? There is no 8398b.

#21 Updated by Vladimir Tsichevski almost 2 years ago

Constantin Asofiei wrote:

What branch exactly? There is no 8398b.

The branch is 8393b, sorry.

#22 Updated by Constantin Asofiei almost 2 years ago

The fixes for the synchronization of contextStorage look OK. But, for getImpl, the synchronization in:

      else if (mode == MODE_SERVER)
      {
         synchronized (lock)
         {
            // use the optimized get from ThreadLocal if it exists

I don't think is OK - please see the 9110a changes, and if these solve the issue you have in this task.

#23 Updated by Greg Shah almost 2 years ago

Code Review 8393b Revision 15375

Minor issue: FWD_CLASSES_PREFIX_LENGHT should be FWD_CLASSES_PREFIX_LENGTH.

Of course, if 9110a resolves this then we will drop 8393b.

#24 Updated by Greg Shah almost 2 years ago

After this fixed and the branch rebased, the new revno is 15389.

BTW, the branch I see is not rebased.

#25 Updated by Vladimir Tsichevski almost 2 years ago

Greg Shah wrote:

After this fixed and the branch rebased, the new revno is 15389.

BTW, the branch I see is not rebased.

I cannot do this due to the problem with trunk :-(. I complained on this already in #7143.

#26 Updated by Constantin Asofiei almost 2 years ago

Vladimir Tsichevski wrote:

Greg Shah wrote:

After this fixed and the branch rebased, the new revno is 15389.

BTW, the branch I see is not rebased.

I cannot do this due to the problem with trunk :-(. I complained on this already in #7143.

Use devsrv01 - checkout both trunk and 8393b, unbind, rebase and push.

#27 Updated by Vladimir Tsichevski almost 2 years ago

I found another problem, which currently causes deadlock in a customer app with 8393b.

The problem is in TemporaryDatabaseManager, when the perUserTempDatabases.PrivateTempDbManager is accessed for modification:

The addition code is:

         synchronized (perUserTempDatabases)
         {
            String tempDbName = TemporaryDatabaseManager.getMyTempDbName();
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ problem here
            if (!perUserTempDatabases.contains(tempDbName))
            {
               ...               
               perUserTempDatabases.add(tempDbName);
            }

And removal:

         String tempDbName = getMyTempDbName();
         synchronized (perUserTempDatabases)
         {
            perUserTempDatabases.remove(tempDbName);
            ...
         }

The removal code is OK: the call to getMyTempDbName() is out of synchronization block, while the addition code causes deadlock:
the call to getMyTempDbName() is inside the synchronization block, in it uses thread-context access, which causes dead-lock with the following two threads:

Daemon Thread [Conversation [0000000B:AppserverProcess]] (Suspended)    
    owns: HashSet<E>  (id=12385)    
    waiting for: Object  (id=12386)    
    TemporaryDatabaseManager$PrivateTempDbManager$1(ContextLocal<T>).getImpl(boolean) line: 529    
    TemporaryDatabaseManager$PrivateTempDbManager$1(ContextLocal<T>).get() line: 467    
    TemporaryDatabaseManager$PrivateTempDbManager.getMyTempDbName() line: 355    
    TemporaryDatabaseManager.getMyTempDbName() line: 233    
    TemporaryDatabaseManager.access$300() line: 95    
    TemporaryDatabaseManager$PrivateTempDbManager.initializeMyTempDatabase() line: 311    
    TemporaryDatabaseManager.initializeMyTempDatabase() line: 183

Daemon Thread [Conversation [0000000A:Process]] (Suspended)    
    owns: Object  (id=12386)    
    waiting for: HashSet<E>  (id=12385)    
    TemporaryDatabaseManager$PrivateTempDbManager.initializeMyTempDatabase() line: 311    
    TemporaryDatabaseManager.initializeMyTempDatabase() line: 183    
    PersistenceFactory.getInstance(Database, DatabaseConfig) line: 208    
    PersistenceFactory.getInstance(Database) line: 147    
    TemporaryBuffer(RecordBuffer).initialize(boolean) line: 7192    
    TemporaryBuffer.initialize(boolean) line: 5992    

Note: this does not mean 8393b is innocent, it is just another source of potential problems.

#28 Updated by Vladimir Tsichevski almost 2 years ago

  • % Done changed from 100 to 90
  • Status changed from Review to WIP

Constantin Asofiei wrote:

The fixes for the synchronization of contextStorage look OK. But, for getImpl, the synchronization in:
[...]
I don't think is OK

In a sense, I agree, this currently causes deadlock in multiple FWD threads. But at least in two places this is because non-deadlock-safe code, which can be (should be?) fixed.

please see the 9110a changes, and if these solve the issue you have in this task.

I re-tested with the latest (rev. 15415) trunk with the changes from 9110a merged, and the #9037 issue still remains.

Now I will try to merge the 9110a changes with 8393b and re-test.

#29 Updated by Vladimir Tsichevski almost 2 years ago

Vladimir Tsichevski wrote:

Constantin Asofiei wrote:

The fixes for the synchronization of contextStorage look OK. But, for getImpl, the synchronization in:
[...]
I don't think is OK

In a sense, I agree, this currently causes deadlock in multiple FWD threads. But at least in two places this is because non-deadlock-safe code, which can be (should be?) fixed.

please see the 9110a changes, and if these solve the issue you have in this task.

I re-tested with the latest (rev. 15415) trunk with the changes from 9110a merged, and the #9037 issue still remains.

Now I will try to merge the 9110a changes with 8393b and re-test.

This did not solve the problem either, we probably still need more locking in ContextLocal and changes in other classes to avoid resulting deadlocks.

#31 Updated by Vladimir Tsichevski over 1 year ago

  • Status changed from WIP to Review
  • % Done changed from 90 to 100

Branch 8393b on rev. 15573 fixes the problem. I (re)tested it with the 8393.p and the big customer application.
I saw no regressions.
Also I observed the root issue on trunk still, so the issue is still actual.
Review, please.

#32 Updated by Greg Shah over 1 year ago

  • reviewer Constantin Asofiei, Hynek Cihlar, Tomasz Domin added

#33 Updated by Constantin Asofiei over 1 year ago

Greg/Vladimir: I agree that the synchronization in ContextLocal is faulty. But I propose to improve and fix the synchronization in another way:
  • use a ReentrantReadWriteLock saved in the SecurityContext instance (Greg, IIRC this is a singleton for each FWD context, right?)
  • in each non-static ContextLocal method, resolve the ReentrantReadWriteLock from the SecurityContext, and:
    • if the method is read-only, then obtain a read lock and lock on that
    • if the method writes, then obtain a write lock and lock on that
  • synchronization on the static ContextLocal fields can be removed completely:
    • contextStorage can be made a synchronized (identity) map
    • dependencies can be made a synchronized TreeMap.

This way I think we can remove the class-level synchronization completely, and I hope it will also show some performance improvement, as the locks will be more granular.

Regarding the changes in 9037a, we still have private synchronized void setMode(), which doesn't seem right; and even as they are, the changes are still built on the previous concept of synchronizing the access to ContextLocal.

#34 Updated by Vladimir Tsichevski over 1 year ago

Constantin Asofiei wrote:

Regarding the changes in 9037a...

Branch 9037a never existed :-(. The change is in 8393b, freshly rebased :-)

#35 Updated by Constantin Asofiei over 1 year ago

Vladimir Tsichevski wrote:

Constantin Asofiei wrote:

Regarding the changes in 9037a...

Branch 9037a never existed :-(. The change is in 8393b, freshly rebased :-)

Sorry, yes, I meant 8393b.

#36 Updated by Greg Shah over 1 year ago

SecurityContext instance (Greg, IIRC this is a singleton for each FWD context, right?)

In most cases, yes it is indeed a dedicated instance per context/session.

I think there is a weird exception for the SecurityCache.initContext (associated with the "system session") which is used as the initial context for any non-unique (i.e. the setInitialSecurityContextWorker() first parameter is false) session processed by SecurityManager.setInitialSecurityContextWorker().

I'm not sure how much we depend on that but I have a sneaking feeling it is a bit of badness waiting to bite us. If that is resolved without regressions, then your plan will work nicely.

#37 Updated by Tomasz Domin over 1 year ago

Review:
Not sure if locking in ContextLocal would introduce even more problems, but I've found that out while working on #9243 where I tried to introduce ReentrantReadWriteLock into ContextLocal.getImpl for MODE_UNSET - I failed so far.
In #9243 the problem was that objects are creating other objects on demand when they are needed and that happens on different threads, and I think here we have similar situation. I am not sure if Constantin's solution would work due to complex dependencies between ContextLocal stored objects - I guess it still would lock if two threads would request write lock.

As an alternative solution I was considering three scenarios:
- pre-initialization - create some default ContextLocal objects on session start - that would require identifying objects that are needed for given session type
- delayed initialization - create objects on request but initialize them when first accessed - that would require some wrappers and not sure if it would prevent deadlocks
- disable object initialization by default in ContextLocal.get and let upstream code handle it, that would require a lot of change of FWD code.

#38 Updated by Greg Shah about 1 year ago

  • Related to Bug #5115: Decide and implement PSTimer optimization added

#39 Updated by Greg Shah 11 months ago

  • Status changed from Review to WIP
  • % Done changed from 100 to 70

There are multiple reviews that have been done, with some issues raised that need to be addressed.

Does the recent work in #10206 (10206b) resolve this issue?

#41 Updated by Vladimir Tsichevski 11 months ago

  • % Done changed from 70 to 100

Greg Shah wrote:

There are multiple reviews that have been done, with some issues raised that need to be addressed.

Does the recent work in #10206 (10206b) resolve this issue?

No, the situation is even worse. I am now getting varying results with nearly every client run.

#42 Updated by Vladimir Tsichevski 11 months ago

Tomasz Domin wrote:

As an alternative solution I was considering three scenarios:
- pre-initialization - create some default ContextLocal objects on session start - that would require identifying objects that are needed for given session type

+1. I believe all objects should be created at session startup in a predetermined order to eliminate the possibility of encountering race conditions.

#43 Updated by Vladimir Tsichevski 10 months ago

Note: the fix for #10206 merged as rev. 16156 has not resolved this issue.

#44 Updated by Hynek Cihlar 5 months ago

Vladimir, what is the status of this issue? It is marked as 100% done, should it be reviewed?

#45 Updated by Vladimir Tsichevski 5 months ago

Hynek Cihlar wrote:

Vladimir, what is the status of this issue? It is marked as 100% done, should it be reviewed?

The 8393b fixes the original problem, but there is a general problem with ContextLocal. IMO most reliable solution is proposed in #9037-42.

Also available in: Atom PDF