Tags

, , , ,

Share it

It’s always fun being on the bleeding edge. I’m being slightly ironic here of course. Right now, I’m trying to hook up a Qt front end to an Erlang application using ZeroMQ. So Visual Basic was a lot easier. Blahblah_click()!

ZeroMQ seems like the most elegant way to connect separate processes and give my Erlang app a python based GUI. So I took the erlzmq2 3.x branch, which should work with the 3.x version of ZeroMQ, and started experimenting.

Of course my application uses OTP and so I’ve created two gen_servers, one for controlling the UI (rendering) and one for receiving user feedback. The GUI will be written in Python, but first I’m testing whether I can get ZeroMQ to do at all what I want within Erlang only.

Good examples of OTP applications using ZeroMQ are rare. The examples included in the ZeroMQ guide are pretty basic and I was missing an important piece of the puzzle. I needed to receive a message in a gen_server using erlzmq:recv(). It wasn’t clear to me initially where to place this receive call. It’s a blocking call, so first I put it in a separate process, linked to the gen_server:

% part of receiver gen_server:
init(_Args) ->
	process_flag(trap_exit, true),
	{ok, Context} = erlzmq:context(),
	{ok, Subscriber} = erlzmq:socket(Context, pull),
	ok = erlzmq:connect(Subscriber, "tcp://127.0.0.1:5561"),
	proc_lib:spawn_link(?MODULE, accept_loop, [Subscriber]),
	State = Subscriber,
    	{ok, State}.

% this runs in a separate process
accept_loop(Subscriber) ->
	{ok, Msg} = erlzmq:recv(Subscriber),
	io:format("Message received: ~p", [Msg]),
	accept_loop(Subscriber),

This worked fine. But in other people’s code, I saw they received messages inside handle_info. When working with OTP, that’s a much more idiomatic way than spawning yet another process for a long running or blocking task.

% from somebody’s gen_fsm (simplified for clarity):
handle_info({zmq, S, _, []}, body, State)->
{ok, Data} = erlzmq:recv(S),
{next_state, StateName, State}.

This was unclear to me: who calls handle_info? The documentation for handle_info says that is called when a message is received by the Erlang process that wasn’t originated from a call or a cast. But there was no “Pid ! Msg” pattern to be found in erlzmq.

The clue came from issue 48 in the erlzmq git repository:
“Active mode currently assumes that self() during the erlzmq2 function execution is what wants to receive the active mode responses, which is normal based on other Erlang “active” message handling.”

Aha! Yes, self() is where I want to handle the message, so I should use active mode! I hadn’t done raw sockets in Erlang before, so this wasn’t something that occurred to me naturally.

Now my code looks like this (note the addition of {active_pid, self()})

init(_Args) ->
	{ok, Context} = erlzmq:context(),
	{ok, Subscriber} = erlzmq:socket(Context, [pull, {active_pid, self()}]),
	ok = erlzmq:connect(Subscriber, "tcp://127.0.0.1:5561"),
	State = Subscriber,
    	{ok, State}.

handle_info({zmq, _Socket, Msg, []}, State) ->
	io:format("I got ponies: ~p!", [Msg]),
	{noreply, State};

Needle, haystack, found!

Possibly the next step will be to get ZeroMQ to work with the fantastic e2 library, but first let’s make some triangles in Erlang and show them on a Qt canvas!