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
From:prestoat2000    Date:2008/07/04    Download   
On further reflection, perhaps you should use RTGC instead of 
direct lock and unlock of `eif_gc_mutex'.

From:prestoat2000    Date:2008/07/04    Download   
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.



From:manus_eiffel    Date:2008/06/30    Status: Analyzed    Download   
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?