Welcome to the Monastery | |
PerlMonks |
Semaphores failing to prevent race conditionby Llew_Llaw_Gyffes (Scribe) |
on Mar 12, 2011 at 21:18 UTC ( [id://892863]=perlquestion: print w/replies, xml ) | Need Help?? |
Llew_Llaw_Gyffes has asked for the wisdom of the Perl Monks concerning the following question: Brethren, I have a Perl application (it's an ICB client, but that doesn't really matter for this purpose) written with multiple threads (I wrote it partly to learn to use threads) and a character-mode Curses interface implemented directly with Curses.pm. The interface is nothing fancy - no boxes, menus, borders, anything like that; just a an input pane occupying (by default, but configurable) the bottom five lines of the terminal, a single status line above it, and the rest of the window above that a scrolling display pane. (Think of ICB as IRC Lite on standalone servers.) There are five threads in all: the master thread; an output thread that handles incoming messages, processes hooks and triggers, and updates the display pane; an input thread that handles key events in a readline-like manner, handles input editing and history, and sends messages; a status thread that maintains the status line; and a logging thread that listens to 9and, when activated, logs to disk) both incoming and outgoing messages. Naturally, with five threads, three of which can update the terminal, there is a need to make sure only one of them is updating it at once. Therefore, I have a semaphore:
And every operation that updates the terminal is wrapped in calls to $output_sem->down() and $output_sem->up(). For instance, an example from within the talk() function of the input thread:
Or. a little further down in talk():
(Here, curs_to_ptr() is a function that calculates from the length of the input buffer where in the input pane the cursor should be, and moves it there.) Meanwhile, all output to the display pane is happening via this function:
While the status line is maintained by a similar but separate function (necessarily, because otherwise, as a result of how the status line update function works, I'd have to have nested $output_sem->down() calls, which wouldn't work too well), also wrapped between $output_sem->down() and $output_sem->up() calls. Now, one would normally expect that this ought to work pretty well, and do a good job of keeping one thread from writing into another's area of the display. And, as long as I was on a single-core machine, it mostly did, though there was occasional display corruption that I never managed to isolate. But about four or five months ago now, I built myself a new, much faster workstation with six cores, and all hell broke loose. The lower portion of the screen should look something like this. And most of the time it does. But sometimes, something steps on something else, and something like this happens. (There's a full-window example of a particularly egregious corruption incident here.) Sorry to make you follow links; I'd have inlined the first two images if permitted, they're small GIFs and only a few K each. As far as I can tell, I've taken every possible precaution. But I cannot eliminate this problem, or even isolate exactly how or why it is happening. This leads me to have to ask whether anyone can answer the following questions: — Are Curses.pm and Thread::Semaphore known for certain to be thread-safe on multi-core processors? (I'm running with Perl 5.12.2, Curses.pm 1.28, threads 1.82, threads::shared 1.36, Threads::Semaphore 2.12.) — Is there anything that immediately leaps out that I have conspicuously overlooked in my efforts to avoid a race condition? — Is there any likely cause I may have overlooked for this display corruption, and if so, what can I do about it?
Back to
Seekers of Perl Wisdom
|
|