Friday, July 25, 2014

The Reactor Pattern in Dart


I purposefully did not include the phrase “object oriented” in the title of Design Patterns in Dart. The reason, of course, was that I hope to explore others software design patterns in the book. Some of the patterns that most interest me have to do with concurrency—not because that is where the action is in Dart, but because I am so weak with them.

Let me start by stating that I do not really understand the difference between the actor model, the reactor pattern, regular old events, or the observer pattern. It is not that I have no idea, but rather that I know that I lack the understanding to be able to teach them or their differences. This is why I hope to include them in the book.

Part of the challenge before me is that Dart is built on many of these patterns. Normal operations under Dart occur in an event loop that (I believe) is the reactor pattern. Isolates are built on the actor model. Streams support publish and subscribe. And so forth. So how do I discuss these patterns when they are the building blocks of the language? I really have no good answer to that question, but I have an idea how I can start thinking about them.

So tonight, I implement the Reactor Pattern as described by Douglas C. Schmidt in his Reactor paper. Since the reactor pattern is running underneath in Dart itself, this may be a little weird, but I'm going to try it anyhow.

I start with the main dispatcher as:
class InitiationDispatcher {
  var events = {};

  static final InitiationDispatcher _dispatcher =
    new InitiationDispatcher._internal();

  factory InitiationDispatcher() {
    return _dispatcher;
  }

  InitiationDispatcher._internal();

  registerHandler(eventHandler, eventType) {
    events.putIfAbsent(eventType, ()=> []);
    events[eventType].add(eventHandler);
  }

  removeHandler(eventHandler, eventType) {}

  handleEvents([int timeout=0]) {
    // ...?
  }
}
Most of that is just setting up a singleton per the article. I will worry about removeHandler() another day. I am not quite sure how to handle events just yet, so I leave that open for a bit. I do know how to register a new handler—for a given type of event and corresponding handler object, I add the object to the list of handlers for that type. Adding objects instead of functions is different than the normal event listeners to which I am accustomed, but I roll with it here.

Then the handle event class is a simple keyboard reader:
import 'dart:io';
import 'InitiationDispatcher.dart';

class HandleKey {
  HandleKey(){
    new InitiationDispatcher().registerHandler(this, 'key');
  }

  handleEvent(eventType) {
    if (eventType == 'key') {
      print('[handled] $handle');
    }
  }

  get handle {
    return stdin.readLineSync();
  }
}
As in the paper, I exploit the singleton nature of InitiationDispatcher to get the one instance and register this new keyboard handler when it is constructed. I am still struggling with the Dart equivalent for a “handle” from the paper. Here, I start with reading from STDIN. If nothing else, this will give me something with which to experiment.

Finally main:
#!/usr/bin/env dart

import 'package:reactor_code/InitiationDispatcher.dart';
import 'package:reactor_code/HandleKey.dart';

main() {
  new HandleKey();

  for(;;) {
    new InitiationDispatcher().handleEvents();
  }
}
I create an instance of the HandleKey class (which registers itself with the reactor dispatcher). Then I enter into the infinite loop that tries to handleEvents().

And I remain stumped here on how to handle events in the reactor loop without stealing data from the actual handler—how can I see that there is stuff to be read from STDIN, but not actually read it? Not knowing what else to try, I define handleEvents() as:
class InitiationDispatcher {
  // ...
  handleEvents([int timeout=0]) {
    var line = stdin.readLineSync();
    print('[handleEvents] $line');
    events['key'].forEach((h)=> h.handleEvent('key'));
  }
}
The important part is that handleEvents() tells the appropriate handlers to do their things (the last line of the method). But actually waiting for an event blocks until a line is ready from STDIN—defeating the entire purpose of the Reactor Pattern. Still, for tracer bullet purposes, it works. If I type 1, 2, 3, 4, and so on, I get:
$ ./bin/reactor.dart
[handleEvents] 1
[handleEvent] 2
[handleEvents] 3
[handleEvent] 4
[handleEvents] 5
[handleEvent] 6
The first event triggers the dispatcher. The second triggers the actual handlers.

So I sort of have a Reactor Pattern in Dart. But it would be nicer to have one that did not block and that better mimics handles and select() from the paper. I do not necessarily need low-level I/O or networking access to accomplish this, but what I have now is insufficient. I will ruminate on that (or if someone has suggestions?) and pick back up tomorrow.


Day #133

No comments:

Post a Comment