Events are not fired when the variable not in global scope, Crash on Windows

Issue #26 invalid
Jost Baron created an issue

If you create an event as a local variable in some function, it won't work - the event will never be fired. If you move that same event to some longer lived variable (class attribute, global variable), it just works.

Tested on:

XUbuntu 14.04.3:

  • PHP 7.0.4
  • ext-event 2.0.1

Windows Server 2012 R2:

  • PHP 7.0.4
  • ext-event 2.0.0

Example, with the bug present:

<?php

function registerEvent(\EventBase $eventBase)
{
    $stdin = fopen('php://stdin', 'r');

    $e = new \Event($eventBase, $stdin, \Event::READ | \Event::PERSIST,
        function ($stream, $events)
        {
            if ($events & Event::READ) {
                $line = fgets($stream);
                if ('exit' === trim($line)) {
                    exit(0);
                }
                printf("Your line: %s\n%s", $line, 'Enter a string: ');
            }
        });
    $e->add();
}

$base = new \EventBase();
registerEvent($base);

echo 'Enter a string: ';
$base->loop();

Fixed example (works on Ubuntu, crashes on Windows, additional info below):

<?php

$e;
$stdin;

function registerEvent(\EventBase $eventBase)
{
    global $e;
    global $stdin;

    $stdin = fopen('php://stdin', 'r');

    $e = new \Event($eventBase, $stdin, \Event::READ | \Event::PERSIST,
        function ($stream, $events)
        {
            if ($events & Event::READ) {
                $line = fgets($stream);
                if ('exit' === trim($line)) {
                    exit(0);
                }
                printf("Your line: %s\n%s", $line, 'Enter a string: ');
            }
        });
    $e->add();
}

$base = new \EventBase();
registerEvent($base);

echo 'Enter a string: ';
$base->loop();

Additional info about crash on Windows:

#!

Problem signature:
  Problem Event Name:   APPCRASH
  Application Name: php.exe
  Application Version:  7.0.4.0
  Application Timestamp:    56e5f8de
  Fault Module Name:    ntdll.dll
  Fault Module Version: 6.3.9600.17031
  Fault Module Timestamp:   530895af
  Exception Code:   c0000005
  Exception Offset: 0000000000065e8e
  OS Version:   6.3.9600.2.0.0.272.79
  Locale ID:    1031
  Additional Information 1: 387f
  Additional Information 2: 387f0042a0cdac2563e4feec2f839920
  Additional Information 3: e59f
  Additional Information 4: e59f38c2e10b121d4c24cd4e95c512bb

Seems like some reference counts are not properly updated?

Comments (1)

  1. Ruslan Osmanov repo owner

    The events are normal PHP objects with normal lifetime. When an event object goes out of scope, or is unset, or assigned to null, or destroyed by other means, it is thus disabled (removed from the event loop, if it is added).

    There are two common approaches: 1) keeping the watchers/events internally despite of the language scope rules 2) keeping internal structures as long as the language variables are alive.

    AFAIK, the pthreads extension also requires to retain references to Thread objects. To make user's life easier, it provides Pool class.

    Perl AnyEvent extension also relies on the scope:

    $ perl <<'EOPS'
    use AnyEvent;
    
    sub test {
    print "test start\n";
    my $w = AnyEvent->timer(after => 1,
    cb => sub { print "timer cb\n"; $cv->send; });
    print "test end\n";
    }
    
    my $cv = AnyEvent->condvar;
    my $w_quit = AnyEvent->timer(after => 3,
    cb => sub { print "Quitting\n"; $cv->send; });
    
    test;
    
    $cv->recv;
    EOPS
    test start
    test end
    Quitting
    

    However, Perl's Event extension keeps references internally.

    So it's just a matter of design.

  2. Log in to comment