views:

424

answers:

2

I have a Perl program that has a GTK2 GUI (via the Gtk2 package). This program also opens a network socket (actually via LWP) in another thread, and continuously makes a request for a certain URL, waiting for an event to happen.

If an event occurs, then its data must be processed and interpreted, and an appropriate callback function used to update the GUI. This is where my program falls down.

Main program:

# Attach to the "message received" event
Foo::hook('msgRx', \&updateMsg);

# ...

Gtk2->main();

sub updateMsg {
    my ($msg) = @_;
    print "New message: $msg\n";
    # append to a GTK TextView -- code is also used elsewhere and works fine
    appendMsg($msg); 
}

And in the module:

# ...
my %hooks = ();
my $ev_pid = undef;

sub hook($&) {
    my ($name, $sub) = @_;
    $hooks{$name} = $sub;
}

sub call_hook {
    my ($name, @args) = @_;
    print ">>> CALLING HOOK $name\n";
    return $hooks{$name}->(@args) if (defined($hooks{$name}));
}

sub eventThread {
    while (1) {
        my $res = $browser->post("$baseurl/events", ['id' => $convid]);
        my $content = $res->content;

        last if ($content eq 'null');

        my $events = from_json($content);
        foreach (@$events) {
            my $ev_type = shift @$_;
            my @ev_args = @$_;
            print "Event type: $ev_type\n";
            print Data::Dumper->Dump([@ev_args]);
            handleEvent($ev_type, @ev_args);
        }
    }
}

sub doConnect() {
    # ...
    $ev_pid = fork;
    if (!defined $ev_pid) {
        print "ERROR forking\n";
        disconnect();
        return;
    }
    if (!$ev_pid) {
        eventThread;
        exit;
    }
}

Now the console output from these is what I expect:

>> Starting...
[["connected"]]
Event type: connected
>>> CALLING HOOK start
[["waiting"]]
Event type: waiting
>>> CALLING HOOK waiting
[["gotMessage", "77564"]]
Event type: gotMessage
$VAR1 = '77564';
>>> CALLING HOOK msgRx
New message: 77564
[["idle"]]
Event type: idle
>>> CALLING HOOK typing
[["gotMessage", "816523"]]
Event type: gotMessage
$VAR1 = '816523';
>>> CALLING HOOK msgRx
New message: 816523
>> User ending connection
null
>>> CALLING HOOK end

However, the GUI TextView does not update. I can only presume that this is because the callback is actually happening in another thread, which has duplicates of the objects.

Any suggestions?

A: 

First of all, when you use fork you are creating another process.

Perl has, by default, the threads module which can create real threads, if your perl was compiled with threads support.

Unfortunately the current perl's thread implementation is far away from what you have on other languages, and I would advise not to use it.

Some references for that are:

perldoc threads
perldoc threads::shared

Good luck!

Igor
I knew that was probably the case. Sadly I can't rely on `threads` being available. Thanks though :-)
Dave
As trendels suggested, you could use IPC to solve your processes communication problem.
Igor
+1  A: 

If you are forking, you need to implement some kind of IPC mechanism between your processes. In this case, a simple socket pair that connects the parent and child processes should suffice. See "Bidirectional Communication with Yourself" in perlipc on how to do this.

If the child process has new data available, just write it to the socket. In the main process, install a listener for the socket (I assume Gtk2 uses Glib under the hood, if so then Glib::IO::add_watch is what you need). If new data is available, the handler will be called and can update your GUI.

trendels
Perfect: GLib::IO::add_watch was just what I needed. I can then get by with the implicit fork in open $fh, '-|' and use that file handle. Thanks!
Dave