I have been working with SwingWorkers for a while and have ended up with a strange behavior, at least for me. I clearly understand that due to performance reasons several in
Having read DSquare's superb answer, and concluded from it that some subclassing would be needed, I've come up with this idea for anyone who needs to make sure all published chunks have been processed in the EDT before moving on.
NB I tried to write it in Java rather than Jython (my language of choice and officially the best language in the world), but it is a bit complicated because, for example, publish is final, so you'd have to invent another method to call it, and also because you have to (yawn) parameterise everything with generics in Java.
This code should be understandable by any Java person: just to help, with self.publication_counter.get(), this evaluates to False when the result is 0.
# this is how you say Worker... is a subclass of SwingWorker in Python/Jython
class WorkerAbleToWaitForPublicationToFinish( javax.swing.SwingWorker ):
# __init__ is the constructor method in Python/Jython
def __init__( self ):
# we just add an "attribute" (here, publication_counter) to the object being created (self) to create a field of the new object
self.publication_counter = java.util.concurrent.atomic.AtomicInteger()
def await_processing_of_all_chunks( self ):
while self.publication_counter.get():
time.sleep( 0.001 )
# fully functional override of the Java method
def process( self, chunks ):
for chunk in chunks:
pass
# DO SOMETHING WITH EACH CHUNK
# decrement the counter by the number of chunks received
# NB do this AFTER dealing with the chunks
self.publication_counter.addAndGet( - len( chunks ) )
# fully functional override of the Java method
def publish( self, *chunks ):
# increment the counter by the number of chunks received
# NB do this BEFORE publishing the chunks
self.publication_counter.addAndGet( len( chunks ))
self.super__publish( chunks )
So in your calling code, you put something like:
engine.update_xliff_task.get()
engine.update_xliff_task.await_processing_of_all_chunks()
PS the use of a while clause like this (i.e. a polling technique) is hardly elegant. I looked at the available java.util.concurrent classes such as CountDownLatch and Phaser (both with thread-blocking methods), but I don't think either would suit for this purpose...
later
I was interested enough in this to tweak a proper concurrency class (written in Java, found on the Apache site) called CounterLatch. Their version stops the thread at await() if a value of an AtomicLong counter is reached. My version here allows to you to either to do that, or the opposite: to say "wait until the counter reaches a certain value before lifting the latch":
NB use of AtomicLong for signal and AtomicBoolean for released: because in the original Java they use the volatile keyword. I think using the atomic classes will achieve the same purpose.
class CounterLatch():
def __init__( self, initial = 0, wait_value = 0, lift_on_reached = True ):
self.count = java.util.concurrent.atomic.AtomicLong( initial )
self.signal = java.util.concurrent.atomic.AtomicLong( wait_value )
class Sync( java.util.concurrent.locks.AbstractQueuedSynchronizer ):
def tryAcquireShared( sync_self, arg ):
if lift_on_reached:
return -1 if (( not self.released.get() ) and self.count.get() != self.signal.get() ) else 1
else:
return -1 if (( not self.released.get() ) and self.count.get() == self.signal.get() ) else 1
def tryReleaseShared( self, args ):
return True
self.sync = Sync()
self.released = java.util.concurrent.atomic.AtomicBoolean() # initialised at False
def await( self, *args ):
if args:
assert len( args ) == 2
assert type( args[ 0 ] ) is int
timeout = args[ 0 ]
assert type( args[ 1 ] ) is java.util.concurrent.TimeUnit
unit = args[ 1 ]
return self.sync.tryAcquireSharedNanos(1, unit.toNanos(timeout))
else:
self.sync.acquireSharedInterruptibly( 1 )
def count_relative( self, n ):
previous = self.count.addAndGet( n )
if previous == self.signal.get():
self.sync.releaseShared( 0 )
return previous
So my code now looks like this:
In the SwingWorker constructor:
self.publication_counter_latch = CounterLatch()
In SW.publish:
self.publication_counter_latch.count_relative( len( chunks ) )
self.super__publish( chunks )
In the thread waiting for chunk processing to stop:
worker.publication_counter_latch.await()