Local synchronization in windows process (critical section)

Posted by dbeavon on 12-Dec-2018 00:28

ABL is single threaded and there is rarely the need to synchronize multiple threads within a process.  The primary form of synchronization that I'm accustomed to (in ABL) is the type of stuff that is done at the database level, where transactions are isolated from "stepping" on each other.

But now we are using PASOE and the ABL sessions within an MS-agent have the potential for conflicts.  These conflicts can happen when ABL sessions are doing work at the same time (particularly when they are interacting with process-wide resources like the CLR bridge). 

Does anyone have an idea how I might prevent ABL sessions from running the same ABL code at the same time (within the same process)?  I'm looking for a solution that is fairly fast (less than a millisecond).  I was going to start looking for ABL primitives (or OS-level wrappers) for thread synchronization but I don't think there are any yet, given that the language is single-threaded.  Another idea I had is to try to exclusively lock a local file via Win32 (see https://knowledgebase.progress.com/articles/Article/P26125 )

Ideally there is an approach for synchronization that is fast and local.  Otherwise I could probably create a OE database record via client/server but I'd like to avoid that since it seems odd to rely on remote client/server requests in order to synchronize the work that is done within a local process.  Hopefully someone already has a solution for creating critical sections in ABL.  Thanks in advance.

Posted by David Abdala on 30-May-2019 12:08

If this is a "one-time" use case, I will just use Windows calls to create and use a CriticalSection or a simple Semaphore.

If this something you are going to use "for real", then I will make an independent program (C, Pascal, Java, whatever not tied to Windows) that you can use to "marshall" whatever lock mechanism you choose to use.

All Replies

Posted by gus bjorklund on 12-Dec-2018 15:48

Just curious: why do you want to prevent sessions in the same agent process from running the same code at the same time?

Posted by David Abdala on 30-May-2019 12:08

If this is a "one-time" use case, I will just use Windows calls to create and use a CriticalSection or a simple Semaphore.

If this something you are going to use "for real", then I will make an independent program (C, Pascal, Java, whatever not tied to Windows) that you can use to "marshall" whatever lock mechanism you choose to use.

Posted by David Abdala on 30-May-2019 12:09

I'm just now realizing that this question is 6 month old, but today I received the email with the original question...

Posted by tpavlovic on 30-May-2019 12:18

Something weird happened yesterday. I received several emails that were posted months ago.

Posted by PhilF on 31-May-2019 02:12

Can you still access a DB from these ABL sessions?  The technique I use for this is to lock a record in a utility table (which I call ResourceLock).  It is possible to create a lock without extending your transaction scope.  A side benefit of this technique is that Progress automatically releases the lock if the process dies prematurely, so you don't have to worry about "stale locks".

I can supply some sample code if it would help.

Posted by dbeavon on 31-May-2019 12:40

@PhilF

Thanks for the tip.  I have used record locking in the past as well.  One of the problems is that the code I'm synchronizing is local to a *single* process (_mproapsv) and it seems overkill to reach out over the network to a remote database (via client/server) in order to synchronize two threads in a single local process.

Another issue with this approach is that the exchanging of control over a record lock takes a very long time.  Locks are withheld from the client that is waiting, even after the lock is released.  I think this is because of the behavior of the NmsgWait startup parameter.    You can read more about that in the following thread:

community.progress.com/.../56505

The only way I found to release the utility table more quickly to another waiting client is to implement custom code that continually spins and tries to lock with "EXCLUSIVE-LOCK NO-WAIT NO-ERROR".  This involves a lot of custom programming that shouldn't be needed, and is wasteful on CPU and network.

Posted by PhilF on 31-May-2019 19:36

I see you're way ahead of me.  Also that I had forgotten key aspects of your request by the time I answered it.  Sorry!

There are slightly smaller O/S-based hammers you can use, like sockets or named pipes (or even INPUT-OUTPUT THROUGH)  .There is also Progress PUBLISH/SUBSCRIBE. I have my doubts as to whether any of these would meet your needs for reliability and speed, but I have never tested them in that way.  They would also require some careful thought if you are trying to synchronize more than 2 threads at once -- though it shouldn't be too hard to make it work.

I suspect that David Abdala is right, and that a call to the O/S is probably what you need.  Though even for production I think you could encapsulate the calls into a Progress subroutine instead of creating an external program.  If you are ambitious, you could even link the necessary calls directly into the Progress executable as new commands, but I don't see why you would want to.  :-)

Best of luck.  I'm curious to hear what solution you eventually arrive at.

Posted by PhilF on 31-May-2019 19:36

I see you're way ahead of me.  Also that I had forgotten key aspects of your request by the time I answered it.  Sorry!

There are slightly smaller O/S-based hammers you can use, like sockets or named pipes (or even INPUT-OUTPUT THROUGH)  .There is also Progress PUBLISH/SUBSCRIBE. I have my doubts as to whether any of these would meet your needs for reliability and speed, but I have never tested them in that way.  They would also require some careful thought if you are trying to synchronize more than 2 threads at once -- though it shouldn't be too hard to make it work.

I suspect that David Abdala is right, and that a call to the O/S is probably what you need.  Though even for production I think you could encapsulate the calls into a Progress subroutine instead of creating an external program.  If you are ambitious, you could even link the necessary calls directly into the Progress executable as new commands, but I don't see why you would want to.  :-)

Best of luck.  I'm curious to hear what solution you eventually arrive at.

Posted by gus bjorklund on 31-May-2019 20:42

> On May 31, 2019, at 3:38 PM, PhilF wrote:

>

> a call to the O/S is probably what you need

That may be the right solution, but know this: liberal use of mutual exclusion primitives is fraught with much difficulty. Getting code that uses them right is very, very, very hard and when it isn't right, things will just stop with no hint of the reason.

It requires warped minds and takes them years to learn how to do it.

Posted by dbeavon on 31-May-2019 20:43

>> I'm curious to hear what solution you eventually arrive at.

My strategy for these types of things is to reach out of ABL into the .Net runtime (via the CLR bridge).  The CLR can be used in-process, and all requests to the CLR use a shared appdomain.  There are some simple primitives in the CLR that I can use for synchronization.

When I first posted the question, the problem was that there was a bug in the CLR bridge itself.  The bug was that the CLR bridge initialization would fail - this was caused by concurrent ABL sessions that were all trying to launch at once.  So it was a "catch-22" - I couldn't use the CLR bridge to synchronize my ABL sessions (threads) because the CLR bridge wasn't being initialized properly in the first place, due to synchronization problems.

This thread is closed