Am Mi., 7. Sept. 2022 um 21:02 Uhr schrieb John Cowan <[email protected]>:

>
>
> On Wed, Sep 7, 2022 at 12:12 PM Marc Nieper-Wißkirchen <
> [email protected]> wrote:
>
>
>> Yes, data structures whose locks are in an abandoned state are
>>> potentially inconsistent and if they are the best one can do with them is
>>> to have them GC'ed.  But this does not necessarily mean that the global
>>> state of a program becomes inconsistent, does it?
>>>
>>
> It means that the future behavior of your program is completely
> unpredictable modulo special cases.
>

Unpredictable if one terminates a random thread, but I still don't see why
it would be unpredictable if only certain threads are terminated.  Think of
a Scheme interpreted and the REPL executes a user-entered expression in a
thread.  Typing CTRL-C should terminate the evaluation (let's forget for a
moment that this usually goes over a signal handler because it doesn't
matter).  A Scheme interpreter can be written in a way so that terminating
the thread does not leave the REPL and the rest of the system in an
inconsistent state.  This is at least the case with existing Scheme
implementations, I think.

In fact, this is possibly the best use case for `thread-terminate!` as it
allows a user to write a useful REPL themselves.  By the way, this is an
argument against what I proposed under the name "critical-section" or
"atomic", so let's drop this.


>   I've probably referred to this before, but see <
> https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html>
> for explanations of why tampering with threads from outside the thread is
> unsafe in the general case.  To quote the most important part:
>
> If any of the objects previously protected by these monitors were in an
> inconsistent state, other threads may now view these objects in an
> inconsistent state. Such objects are said to be *damaged*. When threads
> operate on damaged objects, arbitrary behavior can result. This behavior
> may be subtle and difficult to detect, or it may be pronounced. Unlike
> other unchecked exceptions, ThreadDeath kills threads silently; thus, the
> user has no warning that his program may be corrupted. The corruption can
> manifest itself at any time after the actual damage occurs, even hours or
> days in the future.
>
> Using atomic operations, one can also program the data structures in a way
>> so that they can recover from an abandoned lock.
>>
>
> The data structure may recover, but the code that has already relied on
> the broken data structure cannot.  If your sequence data structure has
> become circular when it was never expected to be, your thread that is
> trying to process it may go into an infinite loop processing what is
> otherwise garbage.  When a time structure of hours-minutes-seconds was
> being updated from 00:00:59 to 00:01:00 and is terminated leaving 00:01:59,
> whatever thread depends on the time concludes that it has missed its
> deadline by a whole minute and panics the entire system to prevent further
> damage.  And so on.
>

The time structure is obviously used by several threads, so it should be
protected by a mutex.  The modifying thread locks the mutex and begins to
update the value.  If it is then terminated, the lock is left abandoned.
Another thread trying to read the time will try to lock the mutex.  But
then an abandoned mutex exception is raised, signaling that the time value
is possibly incorrect.  So the reading thread would not mistakenly assume
that already a minute has passed and the system is still in a consistent
state (the exception can possibly propagate).

If the time structure is still crucial even after the forced termination of
any thread, the protocol could be as follows:  The time structure comes
with a shadow field and an atomic flag.  The mutex is locked when it is
updated by a thread.  Then the shadow field is populated with the updated
current time.  Afterward, the atomic flag is raised.  Then the time is
copied from the shadow field.  Finally, the atomic flag is lowered and the
mutex is unlocked.

When a reading thread tries to lock the mutex but sees an abandoned one, it
checks the state of the atomic flag.  If it is raised, the shadow field
holds a consistent time object (and would be copied onto the actual time).
If it is lowered, the actual time structure is consistent.

All that is needed to make this feasible is that modifications of single
locations in the store are atomic (which is not hard to achieve in a Scheme
system).

In other words, using abandoned mutexes, one can ensure that whatever
thread accesses a data structure finishes the work left over by a
forcefully terminated thread.

Marc (in CC) surely can say much more about this, but I thought that the
idea of abandoned mutexes in SRFI 18 is there so that `terminate-thread!`
can be handled gracefully.

Marc

Reply via email to