backedge checks

Andrew Haley aph at redhat.com
Thu Feb 19 08:40:10 PST 2009


Edward Nevill wrote:

>> Hmm, this doesn't look like it's thread safe to me.  It would make more
>> sense to have a pointer to the branch dispatch table that's updated
>> atomically.
> 
> There is a problem with updating the pointer to the table rather than the contents
> of the table.
> 
> Hopefully, the pointer to the table is residing in an ARM register (or MIPS or ...).
> 
> 	register uintptr_t *dispatch_table = ...
> 
> In order to update the pointer we would first of all need to remove the register specifier,
> then we need to may it a static global, otherwise we cant access it in 'notice_safepoints'.
> For good measure we need to declare it volatile to make sure the compiler cant do anything
> clever. Yeuch (remember this pointer is accessed for every single bytecode).
> 
> I believe the above is thread safe.
> 
> In 'notice_safepoints' the VM state is 'synchronizing'. On return from 'notice_safepoints'
> the VM state is changed to 'synchronized' (provided this was the last thread to synchronise).
> 
> Until the VM state is 'synchronized', the VM cannot make any assumptions as to whether
> the interpreter is holding pointer to objects in registers or local storage.
> 
> Therefore unsafe operations such as GC are prohibited until the VM state is 'synchronized'.
> 
> During execution of 'notice_safepoints' half the handlers may point to safe handlers, half
> may point to unsafe handlers. However, this will not cause any problems. The safe and
> unsafe handlers are compatible in operation, its just the unsafe handlers dont call
> 'SafepointSynchronize::block(THREAD)'.
> 
> Now assume there is a pre-emptive thread swap while in the middle of 'notice_safepoints'.
> The new thread then tries an unsafe operation (eg a GC request) which results in
> 'notice_safepoints' being called again.
> 
> The result is that the table may end up getting updated twice. In the really paranoid case
> every single thread may be sitting in 'notice_safepoints'.
> 
> notice_safepoints() {
> /* <- thread swap occurs here */
> }
> 
> This is no different from the case where 'notice_safepoints' actually updates the table.
> The VM is still not synchronised, and we have to either wait until control returns or
> until someone else calls notice_safepoints. Remember the state is not set to synchronised
> until all threads have synchronised.
> 
> On exit, before calling ignore_safepoints the VM is places in an unsynchronised state.
> Therefore it doesn't matter whether we call the safe or unsafe branch handler. The
> safe branch handler simply does an extra check on 'is_synchronizing' which returns false.
> 
> The only thing that could really ruin your day is if word write is not atomic, and I think
> there is a lot of code that will break is word write is not atomic.

I knew this wasn't right, and I just realized why.  Every processor has its own
data cache, and these caches may not be coherent.  You could potentially wait
for ever before a processor would read the changed dispatch table.

I think this could probably be solved by sending a signal to every thread that
is supposed to move to a safepoint.  If there's any additional processing needed
the signal handler can do it.

Also, the thread updating the table would have to flush its own caches/write buffers/etc.

Finally, unless the table itself is marked as volatile there's no guarantee that
it would be written before the flush.

Andrew.



More information about the zero-dev mailing list