=head1 EV - (noch) ein Event-Modell für Perl =head1 Was? EV ist ein Perl-Modul, das Ereignisgesteuertes Programmieren unterstützt. Darin unterscheidet es sich erst einmal nicht von anderen dedizierten Modulen wie "Event" oder die in jedes Toolkit (Gtk, Tk etc.) eingebaute Ereignisverarbeitung. EV ist des weiteren eine Schnittstelle zur "Libev"-Bibliothek, eine Art Libevent-Klon, und erbt deshalb deren Features (worin sich EV nun von einigen anderen Modulen unterscheidet): =over 4 =item Korrektheit Die meisten Ereignis-Bibliotheken funktionieren gut, solange nichts unvorhergesehenes passiert. Die meisten versagen kläglich, wenn die Systemuhr verstellt wird (Zeitsprünge): Aus einem 10s-Timeout können bei Tk (Event, Glib etc.) problemlos mal Jahre werden, aus einem Tag wenige Sekunden. EV unterstützt sowohl relative Timer ("wecke mich in 10 Minuten auf, egal ob ntpd die Uhr ändert oder nicht) als auch absolute Timer ("wecke mich 2008-10-10 12:17:23.445 auf, egal wie lange das wirklich dauert" oder gar "wecke mich zur nächsten Mitternacht auf"), was kein anderes mir bekanntes Event-Modell unterstützt. Registriert man bei Tk (Event::Lib, ...) zwei Watcher auf denselben Filehandle, so kann einer davon verloren gehen (nicht notwendigerweise immer der ältere oder neuere). Stellt man bei Event die "richtigen" Prioritäten ein, kriegt man eine stetig wachsende Event-Queue die nicht kleiner wird (was allerdings selten in Out-Of-Memory-Situationen endet, denn Event wird vorher zu langsam). Das alles sind Kleinigkeiten - bis das Programm deshalb mal "hängt", 100% Rechenzeit verbraucht oder gar sporadisch versagt. Außerdem haben viele Event-Modelle (Event, Glib, Tk...) ein von Perl abweichendes Objektmodell: verliert man beim Event oder Glib die Referenz auf den Watcher, so hat man bestenfalls ein Memory-Leak. Die EV-Watcher bestehen genau so lange, wie Referenzen darauf zeigen wie jedes andere Perl-Objekt auch. Fork ist ein ganz spezielles Problem: wenige Event-Bibliotheken unterstützen es direkt, vor allem diejenigen, die Epoll, Kqueue o.ä. benutzen, da diese Schnittstellen sehr fork-feindlich sind. EV unterstützt fork indem es den Kernel-Zustand im Kindprozess automatisch wiederherstellt, sofern es benutzt wird. =item Geschwindigkeit EV benutzt "die richtigen" Algorithmen zur Verwaltung: Heaps für die Verwaltung der Timer (keine lineare Liste oder ähnliches wie alle anderen), Arrays, wenn es um viele Watcher geht, Listen, wenn es nur wenige (meist einen) geht. Auch geht Libev nicht vor jedem Pollen alle seine Watcher (Event, Glib, ...) durch um zu sehen ob sie noch alle da sind. Dadurch skaliert EV nicht nur hervorragend in die Bereiche von hunderttausenden Filehandles und Timern (falls man das braucht), sondern ist auch mit wenigen davon sehr schnell (was mindstens genauso wichtig ist). Auch die Perl-Schnittstelle selbst wurde geschrieben, um unnötige Laufzeitkosten zu vermeiden: EV erzeugt einen Watcher immerhin 40 mal schneller als Event und fast viermal so schnell wie Glib, womit EV + AnyEvent immer noch schneller sind als "nacktes" Glib. Auch der Speicherverbrauch ist sehr gering: EV-Watcher sind schlichte geblesste Strings die den Libev-Watcher enthalten. Diese wiederum sind im Vergleich mit anderen Bibliotheken recht klein (weniger als 100 Bytes). =item Skalierbarkeit Nicht nur die Algorithmen, auch die Schnittstelle zum Betriebssystem muss skalierbar sein, und das bedeutet leider auch: nicht portabel. Jedes Betriebssystem braucht sein eigenes Rezept und daher auch sein eigenes Backend: Libev unterstützt Epoll, Kqueue und Solaris' Event Ports. Einige Backends sind sehr begrenzt (Kqueue z.B. ist auf fast allen System kaputt und supported bestenfalls Sockets) aber in andere Backends integrierbar, so kann man z.B. ein Kqueue-Backend (supported nur Sockets zuverlässig auf FreeBSD) in ein "herkömmliches" Poll-basiertes Backend integrieren. =item Watcher-Typen Neben den üblichen I/O Watchern und Timern (derer es zwei grundsätzlich verschiedene Typen in EV gibt), bietet EV noch speziellere Watchertypen für POSIX-Signale, Prozess-Watcher, Idle-Watcher (für "Hintergrundprozesse"), Prepare/Check-Watcher, mit denen man EV-fremde Event-Modelle oder -Benutzer integrieren kann und Stat-Watcher, mit denen man Dateisystemobjekte auf Veränderungen überwachen kann. Ganz spezielle Watcher, um C zu erkennen oder andere Backends zu integrieren gibt es natürlich auch. =item Kompatibilität EV wird von AnyEvent voll unterstützt, so daß Module oder Programme, die AnyEvent benutzen, ohne Quellcodeänderungen sofort mit EV benutzt werden können. =item C-Schnittstelle Das EV-Modul exportiert die komplette Libev-API auch auf XS-Ebene, was so nur noch Event bietet (bei anderen Modulen muss man auf positive Eigenschaften von Shared-Libraries vertrauen). =back =head1 Warum? EV ist, wie die zugrunde liegende Libev, sehr jung (Ende 2007 erschien die erste Version), und die Entstehung war mehr Zufall als Absicht: Seit ca. 1998 benutze ich regelmäßig und viel das Event-Modul, welches problemlos als das ausgereifteste der verfügbaren Event-Module gelten durfte. Im Laufe der Jahre stieß ich auf eine Menge kleinerer Probleme mit anderen Event-Modellen (siehe weiter oben), doch die Anstrengung, eine eigene Bibliothek zu schreiben, wollte ich nicht aufbringen. Speicherverbrauch war eigentlich nie ein Problem, die Beschränkung auf C