Hi there,

The semaphore code looks fine. As Andy noted, things get much more complicated when the code needs to run across multiple concurrent jobs: We might easily run into deadlocks.

Thus, if it turns out that concurrency gets a bigger issue, we would probably embed key/value store updates into our Pending Update List concept, even if it would make it less flexible to use.

Cheers,
Christian


On Sat, Feb 8, 2025 at 12:07 PM Marco Lettere <m.lettere@gmail.com> wrote:

For us it can be solved with custom annotations or even constant strings because it all comes down to the one workflow engine ...


Il sab 8 feb 2025, 11:55 Andy Bunce <bunce.andy@gmail.com> ha scritto:
One problem with this might be that: 
When using the fork-join function it is easy to ensure all the threads have a reference to the same semaphore. 
If these were arbitrary BaseX "jobs" it is not clear how this could be done without explicit support from the BaseX runtime. 
Perhaps it could be done with a new annotation
%basex:semaphore ("my-semaphore") 
that could be applied to functions.

/Andy

On Fri, 7 Feb 2025 at 18:31, Marco Lettere <m.lettere@gmail.com> wrote:

Oh, wow. Looks great Andy. Thanks for suggestion.

Wonder to know what's Christian's opinion on this.

M.

On 07/02/25 16:45, Andy Bunce wrote:
>so if you call store:get, store:put or store:write in the first process, a second process will not wait until the store operations are completed.

In non XQuery contexts a semaphore [1] might be used to ensure that my other threads don't get between a get and put.
In the spirit of blurring the XQuery Java boundaries I tried [2]. It seems to work. Is it dangerous?

[2]
declare namespace Semphore ="java:java.util.concurrent.Semaphore";

declare  function local:config-update($k as xs:string,$v as item(),$sem) {
   Semphore:acquire($sem),
   try{
     let $u:=store:get("config")=> map:put($k, $v)
     return store:put("config",$u)
   }catch * {
     trace("Errrr",$err:description)
   }
   ,Semphore:release($sem)
};

let $sem:=Semphore:new(1,true())

let $s1 := store:put("config", map{})
let $s2 := xquery:fork-join(
  for $i in (1 to 100)
  return function(){
    let $r:=(prof:sleep(10),$i)
    return local:config-update( string($i),$r,$sem) }
  )

return count(map:keys(store:get("config")))


On Tue, 28 Jan 2025 at 14:09, Marco Lettere <m.lettere@gmail.com> wrote:

Ok, thanks for the clarification.

M.

On 28/01/25 15:08, Christian Grün wrote:

Sorry Christian, do you mean *not* synchronized?

With »synchronized«, I meant to refer to a lower level: You will not end up with a corrupt key/value store or with I/O conflicts when accessing and updating the store via multiple threads. However, as you have already observed, multiple operations are not executed in a well-defined order, so if you call store:get, store:put or store:write in the first process, a second process will not wait until the store operations are completed.