On Wed, 03 Jun 2009, Pritpal Bedi wrote:

Hi,

> For the last three hours I am trying to reach some results 
> by applying your code with more permutations and combinations.
> So far I have not reached any solution.
> The first observation is that METHOD :enter() SYNC is not working
> as synchronized. A startup only one programmer should be able to "MAKE
> Coffee"
> while others should be in "NEED Coffee" state. But I get all programmers in
> "MAKE"
> state.

I thought it's expected behavior.
At least such behavior is results of xbase++ documentation you sent
which was also described by Maurilio and other xbase++ gurus.

> As this is reached only when one :enter() then I can safely say that 
> SYNC METHODs are not working with Harbour.

No they works just like were designed. The problem is that we do not have
anyone who knows how xbase++ really works and/or tried to make some real
life tests with xbase++ to detect it and xbase++ documentation you sent
does not give full information about sync methods.

Let's collect the information we have.
>From the xbase++ documentation you sent to this list:
,-------------------------------------------------------------------------
| When an object is visible in multiple threads at the same time,
| the SYNC attribute makes sure that the program code of a method
| is executed entirely in one thread before another SYNC method of
| the same object can be executed in another thread. SYNC methods
| are automatically synchronized when executed by different threads.
| Therefore, when different SYNC methods are called for the same
| object (self) from multiple threads, they are executed in sequential
| order without being interrupted. Only one SYNC method can be executed
| using the same object (self) at any given point in time.
|
| In multi-threaded programs, the SYNC attribute is necessary when a
| method performs multiple operations to achieve a consistent state of
| an object (self). This is the case, for example, when multiple instance
| variables are changed within a method and the object's consistency
| depends on all changes to be completed before the method is executed
| in another thread. The SYNC attribute guarantees that the program code
| of a method will run to completion within a single thread. No other
| thread can execute a SYNC method with the same object before the method
| has returned.
|
| There is one exception to the above stated rule. If thread A has
| suspended program execution of a SYNC method due to waiting for a
| signal, another SYNC method of the same object can be invoked in
| thread B. The suspended thread A will resume with method execution
| after being signalled only when the second SYNC method has run to
| completion in thread B (see the second example below).
`-------------------------------------------------------------------------

Please read the exception above. It means that the SYNC method
CoffeeMachine:enter() should be unblocked inside oProgrammer:takeCoffee()
method when it executes:
   ::coffeeMachine:coffeeIsReady:wait()
suspending program execution. I guess it doesn't. But for sure not in all
cases because such behavior is necessary for example in the xbase++
documentation you sent otherwise it will cause deadlock.

/************************************************************************/
   // Suspending a SYNC method
   // The example demonstrates the program flow of two SYNC methods
   // when one SYNC method is suspended while waiting for a signal.

   PROCEDURE Main
      LOCAL oThreadA := MyThread():new()
      LOCAL oThreadB := Thread():new()
      oThreadA:start()
      oThreadB:start( {|| oThreadA:signal() } )
      ThreadWaitAll( { oThreadA, oThreadB } )
      AEval( oThreadA:aHistory, {|a| QOut( a[1], a[2] ) } )
   RETURN

   CLASS MyThread FROM Thread
   PROTECTED:
      METHOD execute
   EXPORTED:
      VAR aHistory
      VAR lTerminate
      VAR oSyncSignal
      SYNC METHOD add, signal
      INLINE METHOD init
         ::Thread:init()
         ::aHistory    := {}
         ::lTerminate  := .F.
         ::oSyncSignal := Signal():new()
      RETURN
   ENDCLASS

   METHOD MyThread:execute
      Aadd( ::aHistory, { ThreadID(), "starting" } )
      DO WHILE .NOT. ::lTerminate
         ::add()
      ENDDO
      Aadd( ::aHistory, { ThreadID(), "terminating" } )
      ::Quit()
   RETURN self

   METHOD MyThread:add
      AAdd( ::aHistory, { ThreadId(), "suspended" } )
      ::oSyncSignal:Wait(0)
      AAdd( ::aHistory, { ThreadId(), "signalled" } )
   RETURN self

   METHOD MyThread:signal
      AAdd( ::aHistory, { ThreadId(), "before signal" } )
      ::oSyncSignal:signal()
      AAdd( ::aHistory, { ThreadId(), "after signal" } )
      ::lTerminate := .T.
      AAdd( ::aHistory, { ThreadId(), "sleep 2 seconds" } )
      Sleep(200)
      AAdd( ::aHistory, { ThreadId(), "terminating"} )
   RETURN self
/************************************************************************/

So the question is when and what is really unlocked.
Analyzing different examples and your information I guess that it's unlocked
only when oSignal:wait() is called directly from SYNC method. Unlocked is
only the one object and class from which the sync method was executed.
Then locks are restored. Using any intermediate function/procedure or
non sync method disable unlocking.
It means that the description of this mechanism presented by xbase++ users
on this and xharbour devel list is wrong and the additional code in Harbour
and xHarbour (xHarbour supports only class sync methods) used to emulate
this behavior is unnecessary and from both projects we can safely remove it.
The real xbase++ behavior is very simple and can be implemented locally
without introducing synced mutexes to core code.
Good news: the code will be simpler.
Bad news for me and Walter: we lost a lot of time to work on sth what is
unnecessary for anyone.

But before I'll remove synced mutexes from core code I would like to
hear some confirmation of above behavior from xbase++ user who really
knows this language. I'm still guessing so it's possible that I'm wrong.
Below I'm attaching simple test code which can be used for test. It also
checks one signal object behavior which I think xbase++ documentation
wrongly describes:
,-------------------------------------------------------------------------
| There are two methods in the Signal class: :signal() and :wait() . The
| method :signal() triggers a signal, while the :wait() method causes a
| thread to enter a wait state. If a thread executes the :wait() method,
| it waits for the signal and suspends program execution. The thread
| resumes as soon as a second thread executes the :signal() method with
| the same Signal object.
`-------------------------------------------------------------------------

I think that one signal sent before any wait call by any thread is buffered
and in such case at least the one thread which call wait() is not suspended.
It's the conclusion from some other example sent by one of xbase++ users.
Though it looks like a bug it's possible that someone created code which
needs such behavior so I plan to replicate it.
Please make some tests with this code in xbase++ and send the results here.
If all tests shows OK in xbase++ then I guess correctly.
If possible please also make some other tests, f.e. using class sync methods
in different combinations, f.e. with normal sync methods.

best regards,
Przemek



/* test code for SYNC/SIGNAL interactions */
#ifdef __HARBOUR__
   #xtranslate sleep(<n>) => hb_idleSleep(<n>/100)
   #include "hbclass.ch"
#endif

#define N_THREADS 5

static s_nMax := 0
static s_oSingnal
static s_nStop := -1, s_nBuff := 0

proc main()
   local i, o

   ? "start"

   o := mycls():new()

   s_oSingnal := signal():new()

   // check the signal buffering
   s_oSingnal:signal()
   s_oSingnal:signal()

   thread():new():start( "waiter0", o )
   thread():new():start( "waiter0", o )
   sleep( 50 )
   ? "number of buffered signals:", s_nBuff, ;
     iif( s_nBuff == 1, "OK", "WRONG" )
   s_oSingnal:signal()
   sleep( 50 )

   for i := 1 to N_THREADS
      thread():new():start( "waiter", o )
   next

   dotest( 0 )
   ? "0. number of threads in normal method.......:", s_nMax, ;
     iif( s_nMax == N_THREADS, "OK", "WRONG" )

   dotest( 1 )
   ? "1. number of threads in sync method.........:", s_nMax, ;
     iif( s_nMax == N_THREADS, "OK", "WRONG" )

   dotest( 2 )
   ? "2. number of threads in deep sync method....:", s_nMax, ;
     iif( s_nMax == 1, "OK", "WRONG" )

   dotest( 3 )
   ? "3. number of threads in nested normal method:", s_nMax, ;
     iif( s_nMax == 1, "OK", "WRONG" )

   dotest( 4 )
   ? "4. number of threads in nested sync method..:", s_nMax, ;
     iif( s_nMax == N_THREADS, "OK", "WRONG" )

   dotest( -1 )
   wait
return

static proc dotest(n)
   s_nStop := -1
   while s_nMax != 0
      s_oSingnal:signal()
      sleep( 10 )
   enddo
   if n >= 0
      s_nStop := n
      sleep( 50 )
   endif
return

function waiter0( o )
   o:enter0()
   o:inc( @s_nBuff )
return nil

function waiter( o )
   while s_nStop != 0; sleep( 1 ); enddo
   while s_nStop == 0
      o:enter0()
   enddo
   while s_nStop != 1; sleep( 1 ); enddo
   while s_nStop == 1
      o:enter1()
   enddo
   while s_nStop != 2; sleep( 1 ); enddo
   while s_nStop == 2
      o:enter2()
   enddo
   while s_nStop != 3; sleep( 1 ); enddo
   while s_nStop == 3
      o:enter3()
   enddo
   while s_nStop != 4; sleep( 1 ); enddo
   while s_nStop == 4
      o:enter4()
   enddo
return nil

static proc setsignal()
   s_oSingnal:wait()
return

class mycls
exported:
   method enter0
   sync method enter1
   sync method enter2
   sync method enter3
   sync method enter4
   sync method inc
   sync method dec
endclass

method mycls:inc( n )
return ++n

method mycls:dec( n )
return --n

method mycls:enter0()
   ::inc( @s_nMax )
   s_oSingnal:wait()
   ::dec( @s_nMax )
return self

method mycls:enter1()
   ::inc( @s_nMax )
   s_oSingnal:wait()
   ::dec( @s_nMax )
return self

method mycls:enter2()
   ::inc( @s_nMax )
   setsignal()
   ::dec( @s_nMax )
return self

method mycls:enter3()
   ::enter0()
return self

method mycls:enter4()
   ::enter1()
return self
_______________________________________________
Harbour mailing list
[email protected]
http://lists.harbour-project.org/mailman/listinfo/harbour

Reply via email to