PR# 14519 Blocking C externals that can raise exception not thread-safe
Problem Report Summary
Submitter: prestoat2000
Category: Runtime
Priority: Medium
Date: 2008/06/24
Class: Bug
Severity: Serious
Number: 14519
Release: 6.2.73753
Confidential: No
Status: Analyzed
Responsible:
Environment: Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.8.1.13) Gecko/20080328 Firefox/2.0.0.13
Solaris 10 on SPARC
Synopsis: Blocking C externals that can raise exception not thread-safe
Description
Based on code inspection only, it looks like blocking C external routines that can raise an exception are not thread-safe. While executing the C code, the GC status of the thread is EIF_THREAD_BLOCKED, so a GC cycle could be running. Blocking C externals that can call `eise_io' are a problem. This routine calls `eraise', which can call `exget', which can call `stack_extend', which calls `cmalloc', which calls `eif_rt_xmalloc. Routine `eif_rt_xmalloc' can manipulate the free list while GC is running, since it uses `eif_free_list_mutex' for synchronization. The blocking C externals that have this problem that I could find include most or all of the ones in FILE and CONSOLE. The `system_call' and `asynchronous_system_call' routines might also have this problem if they are ever changed to raise an exception for error conditions. In addition, any blocking C external that can call a routine that can't get enough memory and calls `enomem' can also have a problem. One possible solution in these unusual error cases is to change thread GC status back to EIF_THREAD_RUNNING and lock/unlock `eif_gc_mutex' to be certain the change of status is seen before raising an exception.
To Reproduce
Problem Report Interactions
On further reflection, perhaps you should use RTGC instead of direct lock and unlock of `eif_gc_mutex'.
Yes, that's what I had in mind. Routine `eraise' (I assume everything goes through this routine to raise an exception) could call EIF_ENTER_EIFFEL at the beginning, then lock and unlock `eif_gc_mutex' to ensure synchronization but only if multithreaded. In other words, at beginning of `eraise' add: EIF_ENTER_EIFFEL; GC_THREAD_PROTECT(EIF_GC_MUTEX_LOCK); GC_THREAD_PROTECT(EIF_GC_MUTEX_UNLOCK); The disadvantage is you have to lock and unlock the mutex whenever an exception is raised. So perhaps the above 3 lines should be done only if the thread GC status is not already EIF_THREAD_RUNNING. Not sure if `eraise' can be called when thread GC status is EIF_THREAD_GC_RUNNING or what should be executed in that case. Seems right, but my brain is a bit fuzzy after a 7 day backpacking trip.
I'm thinking that we could simply call EIF_ENTER_EIFFEL and then perform the lock/unlock of the `eif_gc_mutex' prior to raising the exception in `eraise'. Is this what you have in mind?