#1024 Using actors to make an FWT Telnet

tactics Sun 14 Mar 2010

I'm trying to write a simple telnet client using actors and fwt so I can play with POP3 and IMAP email protocols. I'm having trouble.

The hard part is I don't know conceptually how I should model everything. Right now, it looks like this:

  • a window class
  • a screen updater actor
  • a server listener actor

A subclass of window with two fwt::Texts, one for input and one for output.

The input textbox has an onKeyDown listener. On key down, it takes all the text from the input box and sends it to the screen updater actor.

The screen updater actor has an Unsafe of the output textbox. It overrides Actor.receive to take whatever message it gets and append it to the end of the output textbox.

The listener actor simply enters into an endless loop, reading lines from standard input. When it gets a line, it sends that line as a message to the screen updater.

The problem is that the output box never seems to update. The screen updater actor receives the message. I call outBox.text += msg and outBox.repaint, but still, nothing. If I add echo(msg), the message comes through just fine.

Can anyone explain this behavior? Is there a more idiomatic way of doing this sort of thing?

Also, on thread blocking, my actual implementation is using Java's SSLSockets through FFI. Instead of standard input, the server listener actor is reading from a BufferedReader. I believe (but I'm not certain) these things have trouble waking up when they block. Is there anything I should know about using FFI with actors?

KevinKelley Sun 14 Mar 2010

I call outBox.text += msg and outBox.repaint,

The only thing that jumps out at me is, you need to go through Desktop.callAsync whenever you update UI from actor threads. And since your window isn't immutable, the idiomatic thing is, in your window constructor, stash a handle to the window in Actor.locals under a const identifier, and use that when you callAsync.

Actor.locals["myoutbox"] = outBox   // in window constructor

and

Desktop.callAsync |->| {  // from actor thread, pass a closure with update code
  outbox := Actor.locals["myoutbox"] as Text
  outbox.text += msg
}

or code to that effect (this is untested, off top of my head).

The clock example in fan/examples/fwt does it like this.

Unsafe I guess might work, but it sounds, well, unsafe. :-)

brian Sun 14 Mar 2010

SWT is single threaded only, so in general if to try to do anything from another thread it will throw an exception. Whenever you are having Actor problems, make sure you are dumping/logging exceptions.

Kevin's clock example is great sample code.

Another bit of code you might want to take a look at is the Console code in flux which uses callAsync to write the output from an external process back to a text box.

qualidafial Sun 14 Mar 2010

Could this be made easier by making Desktop expose an actor pool for interacting with the UI thread? Then we can use the same Actor API in fwt as we do everywhere else.

tactics Sun 14 Mar 2010

@KevilKelly Desktop.callAsync does the trick. I remember seeing that call a while ago in the docs, but I didn't quite understand what it's use was.

Are thread local objects supposed to be immutable? Or is it just a contract that when an object is stored in the locals hash, that actor owns that object?

@brian

SWT is single threaded only

This is really good to know.

make sure you are dumping/logging exceptions.

That was the weird part. I wasn't getting any exception. It was silently ignoring my UI calls. The thread-safe echo statements continued to output everything I typed to the input box or stdin.

Kevin's clock example is great sample code.... Another bit of code you might want to take a look at is the Console code in flux I'll check it out :)

KevinKelley Sun 14 Mar 2010

... a contract that when an object is stored in the locals hash, that actor owns that object?

yep... Any non-constant data has to live on one thread only. Communicating between threads is by passing immutable copies. So, any variables you have, need a place to live. If you've got a GUI, you can let your gui hold on to their data, but if you've got actors, they can't access it (even by making calls to a mywidget ) except by sending around immutable messages or immutable closures. Thus, the trick of giving your gui element a const identifier, which the actor can then refer to.

I wasn't getting any exception.

Exceptions that happen in a different thread (inside your Actor.receive method) don't get printed, so you'll get those "silent failures" until you get in the habit of wrapping a try { ... } catch (Err e) {e.trace} or something, around threaded code.

Login or Signup to reply.