Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#5055 closed bug (wontfix)

STM Exception "BlockedIndefinitelyOnSTM" throws to wrong thread

Reported by: guest Owned by:
Priority: normal Milestone:
Component: Compiler Version: 7.0.3
Keywords: stm Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty:
Test Case: Blocked By:
Blocking: Related Tickets:

Description

This might be two bugs. The attached test file (tvarTest.hs) aims to use a separate thread catching a "BlockedIndefinitelyOnSTM" exception to adjust the state of another TVar (a poor mans finalizer on TVars).

More concretely, with TVars "b" and "i" I have a thread (running "gcTVar") retrying on a read of "i" then alters "b" if/when the BlockIndefinitelyOnSTM exception is received. My main thread waits for the state of "b" to change (which is should, due to "gcTVar").

Unfortunately, compiling with GHC (6.12.3, 7.0.1, 7.0.3, threaded or not, optimized or not, it all behaves the same) the main thread receives the exception and not the thread running "gcTVar".

The behavior is different in GHCi. GHCi seems to operate correctly.

Attachments (1)

tvarTest.hs (930 bytes) - added by guest 3 years ago.
A simple test that exhibits the bug

Download all attachments as: .zip

Change History (5)

Changed 3 years ago by guest

A simple test that exhibits the bug

comment:1 Changed 3 years ago by simonmar

  • Resolution set to wontfix
  • Status changed from new to closed

GHC is behaving correctly here. It has figured out that the entire program is deadlocked and has therefore sent both threads the BlockedIndefinitelyOnSTM exception. It's just the way reachability works in the GC: neither thread is reachable from a root, so both are considered to be deadlocked.

It's true that if we were to wake up just the child thread, then it could wake up the main thread. But that's a difficult property for the GC determine - once it has found two threads to be deadlocked, which one should it try to wake up?

comment:2 follow-up: Changed 3 years ago by guest

I suppose I thought it would somehow be determined the following facts based on the exact TVars:

blkB (threads blocked on "TVar b") [1]
blkI (threads blocked on "TVar i") [2]
refB (threads with references to TVar b) [1,2]
refI (threads with references to TVar i) [2]

And based on these facts it could, for each tvar with a thread blocked on it, send a signal to the thread if it is also the only thread with a reference.

The current response seems necessary to deal with situations such as:

blkA [1]
blkB [2]
refA [1,2]
refB [1,2]

Perhaps a hybrid could be built? If all threads are STM blocked and there exists threads blocked on a TVar for which it has the only reference then send an exception to those threads. Otherwise, send an exception to all the threads. How practical is this? (pretend for a moment that I would do the work, if that lie gets you to expand on the topic more ;-) )

comment:3 Changed 3 years ago by guest

Sorry for the dup, that information (properly formated) I hoped the RTS would determine is:

blkB (threads blocked on "TVar b") [1]
blkI (threads blocked on "TVar i") [2]
refB (threads with references to TVar b) [1,2]
refI (threads with references to TVar i) [2]

And the current method of throwing exceptions to each thread seems necessary to deal with situations such as:

blkA [1]
blkB [2]
refA [1,2]
refB [1,2]

comment:4 in reply to: ↑ 2 Changed 3 years ago by simonmar

Replying to guest:

Perhaps a hybrid could be built? If all threads are STM blocked and there exists threads blocked on a TVar for which it has the only reference then send an exception to those threads. Otherwise, send an exception to all the threads. How practical is this? (pretend for a moment that I would do the work, if that lie gets you to expand on the topic more ;-) )

I think what you're asking for is a strongly-connected-component analysis of the graph of threads. Then you could identify the thread(s) that have no references to them, and wake those up. That would be the minimal set of threads that you could wake up.

Unfortunately, SCC requires at least two traversals of the object graph (one with the pointers reversed), plus extra information would need to be stored per-thread. This is pretty hard to do in the context of the existing GC.

Note: See TracTickets for help on using tickets.