(Part 1) Thread-safety Concerns
(Part 1) Thread-safety Concerns
- Subject: (Part 1) Thread-safety Concerns
- From: Steve Klingsporn <email@hidden>
- Date: Sat, 2 Nov 2002 23:47:13 -0600
Here is what I'm using threads for in my Cocoa-Java application, my
justification for using them, and what I've encountered that can go
wrong with Cocoa not being thread-safe. I'll also discuss my next
planned project, why it needs threads, and why the user interface
should be thread-safe (one way or another).
- I use a "Listener" thread subclass that is associated with a
ServerSocket. The thread blocks on the "accept()" method, which
listens on an incoming port for game connection requests. The thread
continues when an incoming connection is received, calling the
constructor for my "Connection" thread subclass, which takes the socket
from the Listener and blocks on a socket read.
- I use a "Connection" thread subclass that is associated with a
Socket. The thread blocks on a "readLine()" call from my
BufferedReader that is associated with the input stream for this
socket. Since Java 1.3.1 and earlier don't have non-blocking I/O, you
need to associate a thread with a socket to have an acceptable user
experience.
- I use a "MoverThread" thread subclass that is associated with my
"AtaxxView" NSView subclass that draws my game board. The
"MoverThread"'s job is to wait() on a boolean, and when a move is
"push()"ed to the MoverThread, notify() is called, the thread takes
care of animating the move (from the computer, computer demo, or
opponent Connection.
- I use a "RosterThread" thread subclass that periodically opens a
socket connection to a "roster server" at some known address. The
purpose of the RosterThread is to update the user's name, display
comment, and IP address before the roster server removes the user's
entry from the Hashtable of users. RosterThread sleep()'s between the
time it is called on to do something and the next polling interval.
Here are where these threads need to interact with the user interface
from the thread-side, instead of due to some user interaction in the UI:
Listener needs to cause a sheet to be thrown up to ask the user if she
wants to accept an incoming connection after the proper handshake
message is received from the socket. Throwing up a sheet is not a
thread-safe operation. Sometimes it works, and sometimes Cocoa crashes
somewhere in the Objective Sea. This is an operation that can
"elegantly" be implemented using an application-defined NSEvent
subtype, so I handle this situation in this manner.
Connection sends incoming one-liner "messages" to the members of its
"handler" vector, which implement the "MessageHandler" interface. In
this case, the handler is AtaxxController, my way-too-overloaded
controller class that manages interaction between the game, "AtaxxNode"
(the platform-independent network class), and "AtaxxGame" (the
platform-independent game model). Connection can generate messages
that necessitate that the game board be updated. These appear to be
thread-safe in that they do drawing into an offscreen NSImage and then
call display() or displayRect() on my "AtaxxView" NSView subclass. I
cannot safely implement this functionality with custom NSEvents because
if the user is doing other things, the events can arrive late, well up,
or (nightmare scenario) perhaps not at all. The result is a
clumsily-updated board view and jittery game experience. Therefore, I
leave these called from the Connection thread, and all seems to be
well. Updating the score NSTextFields, of which there are 2 for each
player, overlaid atop each other to provide the "etched" look atop my
brushed metal textured window is not thread-safe. I do three things to
these when I update the scores: I call setStringValue() on each pair
of NSTextField instances to set the name, score, wins and matches
played, and I change the colors of each pair, either making them look
darker in front or "etched" by messing with their colors. Then, I call
display() on them, because sometimes for some reason, they get a little
visually out of sync, and the illusion is blown. Something here is not
thread-safe, so I'm going to have to move these into NSEvent land.
Connection also can receive incoming chat messages. Incoming chat
messages get spliced with the user's name, turned into a
NSAttributedString, and appended to my conversation NSView. I then
scroll the NSTextView to the bottom so the user doesn't have to
manually scroll the view in order to read what was said. It appears
(though I have read differently) that appending the NSAttributedString
is okay to do from the Connection thread, but I often get into problems
scrolling the conversation NSTextView, so I moved the scrolling part
into NSEvent land. I could move the append there as well and store the
NSAttributedString in a Vector of them, but this doesn't seem to crash
or anything, so I haven't done so. If I scroll the view from the
Connection thread while clicking on the enclosing NSTabView, my app
explodes, so I scroll from NSEvent land.
Roster updates can cause the NSTableView that shows the users online on
the roster server's data source to update. I do this from NSEvent
land. Something is still blowing up in my NSTableView on application
launch, but this is happening to other people on cocoa.mamasam.com (one
of the two mailing lists). There haven't been any good answers or
resolutions to these people's problems, so I assume it's a Cocoa bug.
It's hard to reproduce, happens very rarely, generally at start-up, so
I keep an eye out for it, but I don't assume it's a threading issue as
it happens from the main thread.
So a lot of stuff is going on in my app at any given time. 99% of the
time, everything's cool. Every once in a while, something blows up and
it's generally hard to track down because it won't happen again for
some time, and when it does happen, I invariably don't have
System.out.println()'s sprinkled in the right places to find out where
I am. Looking at the Objective C stack traces in my crash logs helps
somewhat to determine where things went bad, but upon looking at my
code, I can't find where the problems are. I will find them, but it's
a lengthy process. My conscience burns when my wimpy $5 app that I've
spent a good deal of time and energy on bombs the app. Java's not
supposed to do this.
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.