Event loop

Last updated

In computer science, the event loop is a programming construct or design pattern that waits for and dispatches events or messages in a program. The event loop works by making a request to some internal or external "event provider" (that generally blocks the request until an event has arrived), then calls the relevant event handler ("dispatches the event"). The event loop is also sometimes referred to as the message dispatcher, message loop, message pump, or run loop.

Contents

It is also a common method used to implement servers like web servers.

The event-loop may be used in conjunction with a reactor, if the event provider follows the file interface, which can be selected or 'polled' (the Unix system call, not actual polling). The event loop almost always operates asynchronously with the message originator.

When the event loop forms the central control flow construct of a program, as it often does, it may be termed the main loop or main event loop. This title is appropriate, because such an event loop is at the highest level of control within the program.

Message passing

Message pumps are said to 'pump' messages from the program's message queue (assigned and usually owned by the underlying operating system) into the program for processing. In the strictest sense, an event loop is one of the methods for implementing inter-process communication. In fact, message processing exists in many systems, including a kernel-level component of the Mach operating system. The event loop is a specific implementation technique of systems that use message passing.

Alternative designs

This approach is in contrast to a number of other alternatives:

Usage

Due to the predominance of graphical user interfaces, most modern applications feature a main loop. The get_next_message() routine is typically provided by the operating system, and blocks until a message is available. Thus, the loop is only entered when there is something to process.

function main     initialize()     while message != quit         message := get_next_message()         process_message(message)     endwhileendfunction

File interface

Under Unix, the "everything is a file" paradigm naturally leads to a file-based event loop. Reading from and writing to files, inter-process communication, network communication, and device control are all achieved using file I/O, with the target identified by a file descriptor. The select and poll system calls allow a set of file descriptors to be monitored for a change of state, e.g. when data becomes available to be read.

For example, consider a program that reads from a continuously updated file and displays its contents in the X Window System, which communicates with clients over a socket (either Unix domain or Berkeley):

defmain():file_fd=open("logfile.log")x_fd=open_display()construct_interface()whileTrue:rlist,_,_=select.select([file_fd,x_fd],[],[]):iffile_fdinrlist:data=file_fd.read()append_to_display(data)send_repaint_message()ifx_fdinrlist:process_x_messages()

Handling signals

One of the few things in Unix that does not conform to the file interface are asynchronous events (signals). Signals are received in signal handlers, small, limited pieces of code that run while the rest of the task is suspended; if a signal is received and handled while the task is blocking in select(), select will return early with EINTR; if a signal is received while the task is CPU bound, the task will be suspended between instructions until the signal handler returns.

Thus an obvious way to handle signals is for signal handlers to set a global flag and have the event loop check for the flag immediately before and after the select() call; if it is set, handle the signal in the same manner as with events on file descriptors. Unfortunately, this gives rise to a race condition: if a signal arrives immediately between checking the flag and calling select(), it will not be handled until select() returns for some other reason (for example, being interrupted by a frustrated user).

The solution arrived at by POSIX is the pselect() call, which is similar to select() but takes an additional sigmask parameter, which describes a signal mask. This allows an application to mask signals in the main task, then remove the mask for the duration of the select() call such that signal handlers are only called while the application is I/O bound. However, implementations of pselect() have not always been reliable; versions of Linux prior to 2.6.16 do not have a pselect() system call, [1] forcing glibc to emulate it via a method prone to the very same race condition pselect() is intended to avoid.

An alternative, more portable solution, is to convert asynchronous events to file-based events using the self-pipe trick , [2] where "a signal handler writes a byte to a pipe whose other end is monitored by select() in the main program". [3] In Linux kernel version 2.6.22, a new system call signalfd() was added, which allows receiving signals via a special file descriptor.

Implementations

HTML/Javascript

A web page and its JavaScript typically run in a single-threaded web browser process. The browser process deals with messages from a queue one at a time. A JavaScript function or another browser event might be associated with a given message. When the browser process has finished with a message, it proceeds to the next message in the queue.

Windows applications

On the Microsoft Windows operating system, a process that interacts with the user must accept and react to incoming messages, which is almost inevitably done by a message loop in that process. In Windows, a message is equated to an event created and imposed upon the operating system. An event can be user interaction, network traffic, system processing, timer activity, inter-process communication, among others. For non-interactive, I/O only events, Windows has I/O completion ports. I/O completion port loops run separately from the Message loop, and do not interact with the Message loop out of the box.

The "heart" of most Win32 applications is the WinMain() function, which calls GetMessage() in a loop. GetMessage() blocks until a message, or "event", is received (with function PeekMessage() as a non-blocking alternative). After some optional processing, it will call DispatchMessage(), which dispatches the message to the relevant handler, also known as WindowProc. Normally, messages that have no special WindowProc() are dispatched to DefWindowProc, the default one. DispatchMessage() calls the WindowProc of the HWND handle of the message (registered with the RegisterClass() function).

Message ordering

More recent versions of Microsoft Windows guarantee to the programmer that messages will be delivered to an application's message loop in the order that they were perceived by the system and its peripherals. This guarantee is essential when considering the design consequences of multithreaded applications.

However, some messages have different rules, such as messages that are always received last, or messages with a different documented priority. [4]

X Window System

Xlib event loop

X applications using Xlib directly are built around the XNextEvent family of functions; XNextEvent blocks until an event appears on the event queue, whereupon the application processes it appropriately. The Xlib event loop only handles window system events; applications that need to be able to wait on other files and devices could construct their own event loop from primitives such as ConnectionNumber, but in practice tend to use multithreading.

Very few programs use Xlib directly. In the more common case, GUI toolkits based on Xlib usually support adding events. For example, toolkits based on Xt Intrinsics have XtAppAddInput() and XtAppAddTimeout().

Please note that it is not safe to call Xlib functions from a signal handler, because the X application may have been interrupted in an arbitrary state, e.g. within XNextEvent. See for a solution for X11R5, X11R6 and Xt.

GLib event loop

The GLib event loop was originally created for use in GTK but is now used in non-GUI applications as well, such as D-Bus. The resource polled is the collection of file descriptors the application is interested in; the polling block will be interrupted if a signal arrives or a timeout expires (e.g. if the application has specified a timeout or idle task). While GLib has built-in support for file descriptor and child termination events, it is possible to add an event source for any event that can be handled in a prepare-check-dispatch model.

Application libraries that are built on the GLib event loop include GStreamer and the asynchronous I/O methods of GnomeVFS, but GTK remains the most visible client library. Events from the windowing system (in X, read off the X socket) are translated by GDK into GTK events and emitted as GLib signals on the application's widget objects.

macOS Core Foundation run loops

Exactly one CFRunLoop is allowed per thread, and arbitrarily many sources and observers can be attached. Sources then communicate with observers through the run loop, with it organising queueing and dispatch of messages.

The CFRunLoop is abstracted in Cocoa as an NSRunLoop, which allows any message (equivalent to a function call in non-reflective runtimes) to be queued for dispatch to any object.

See also

Related Research Articles

<span class="mw-page-title-main">Operating system</span> Software that manages computer hardware resources

An operating system (OS) is system software that manages computer hardware and software resources, and provides common services for computer programs.

DNIX is a discontinued Unix-like real-time operating system from the Swedish company Dataindustrier AB (DIAB). A version named ABCenix was developed for the ABC 1600 computer from Luxor. Daisy Systems also had a system named Daisy DNIX on some of their computer-aided design (CAD) workstations. It was unrelated to DIAB's product.

In computer programming, event-driven programming is a programming paradigm in which the flow of the program is determined by events such as UI events from mice, keyboards, touchpads and touchscreens or network events. Non-user initiated events can involve sensor inputs, or be programmatically generated from other programs or threads. Event-driven programming is the dominant paradigm used in graphical user interfaces and other applications that are centered on performing certain actions in response to user input. This is also true of programming for device drivers. It is also commonly used for network servers like web servers.

Berkeley sockets is an application programming interface (API) for Internet sockets and Unix domain sockets, used for inter-process communication (IPC). It is commonly implemented as a library of linkable modules. It originated with the 4.2BSD Unix operating system, which was released in 1983.

In computer science, message queues and mailboxes are software-engineering components typically used for inter-process communication (IPC), or for inter-thread communication within the same process. They use a queue for messaging – the passing of control or of content. Group communication systems provide similar kinds of functionality.

In Unix and Unix-like computer operating systems, a file descriptor is a process-unique identifier (handle) for a file or other input/output resource, such as a pipe or network socket.

Signals are standardized messages sent to a running program to trigger specific behavior, such as quitting or error handling. They are a limited form of inter-process communication (IPC), typically used in Unix, Unix-like, and other POSIX-compliant operating systems.

<span class="mw-page-title-main">GDK</span> Software library

GDK is a library that acts as a wrapper around the low-level functions provided by the underlying windowing and graphics systems. GDK lies between the display server and the GTK library, handling basic rendering such as drawing primitives, raster graphics (bitmaps), cursors, fonts, as well as window events and drag-and-drop functionality.

In computer science, asynchronous I/O is a form of input/output processing that permits other processing to continue before the I/O operation has finished. A name used for asynchronous I/O in the Windows API is overlapped I/O.

<span class="mw-page-title-main">Xlib</span> Client library for the X Window System

Xlib is an X Window System protocol client library written in the C programming language. It contains functions for interacting with an X server. These functions allow programmers to write programs without knowing the details of the X protocol.

libevent is a software library that provides asynchronous event notification. The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. libevent also supports callbacks triggered by signals and regular timeouts.

In computer networking, STREAMS is the native framework in Unix System V for implementing character device drivers, network protocols, and inter-process communication. In this framework, a stream is a chain of coroutines that pass messages between a program and a device driver. STREAMS originated in Version 8 Research Unix, as Streams.

The X Window System core protocol is the base protocol of the X Window System, which is a networked windowing system for bitmap displays used to build graphical user interfaces on Unix, Unix-like, and other operating systems. The X Window System is based on a client–server model: a single server controls the input/output hardware, such as the screen, the keyboard, and the mouse; all application programs act as clients, interacting with the user and with the other clients via the server. This interaction is regulated by the X Window System core protocol. Other protocols related to the X Window System exist, both built at the top of the X Window System core protocol or as separate protocols.

The message loop is an obligatory section of code in every program that uses a graphical user interface under Microsoft Windows. Windows programs that have a GUI are event-driven. Windows maintains an individual message queue for each thread that has created a window. Usually only the first thread creates windows. Windows places messages into that queue whenever mouse activity occurs on that thread's window, whenever keyboard activity occurs while that window has focus, and at other times. A process can also add messages to its own queue. To accept user input, and for other reasons, each thread with a window must continuously retrieve messages from its queue, and act on them. A programmer makes the process do that by writing a loop that calls GetMessage, and then calls DispatchMessage, and repeats indefinitely. This is the message loop. There usually is a message loop in the main program, which runs on the main thread, and additional message loop in each created modal dialog. Messages for every window of the process pass through its message queue, and are handled by its message loop. A message loop is one kind of event loop.

In programming and software design, an event is an action or occurrence recognized by software, often originating asynchronously from the external environment, that may be handled by the software. Computer events can be generated or triggered by the system, by the user, or in other ways. Typically, events are handled synchronously with the program flow; that is, the software may have one or more dedicated places where events are handled, frequently an event loop.

The reactor software design pattern is an event handling strategy that can respond to many potential service requests concurrently. The pattern's key component is an event loop, running in a single thread or process, which demultiplexes incoming requests and dispatches them to the correct request handler.

<span class="mw-page-title-main">Grand Central Dispatch</span> Technology developed by Apple Inc

Grand Central Dispatch, is a technology developed by Apple Inc. to optimize application support for systems with multi-core processors and other symmetric multiprocessing systems. It is an implementation of task parallelism based on the thread pool pattern. The fundamental idea is to move the management of the thread pool out of the hands of the developer, and closer to the operating system. The developer injects "work packages" into the pool oblivious of the pool's architecture. This model improves simplicity, portability and performance.

In computing, Nano-X is a windowing system which is full featured enough to be used on a PC, an embedded system or a PDA. It is an Open Source project aimed at bringing the features of modern graphical windowing environments to smaller devices and platforms. The project was renamed from Microwindows due to legal threats from Microsoft regarding the Windows trademark.

Kqueue is a scalable event notification interface introduced in FreeBSD 4.1 in July 2000, also supported in NetBSD, OpenBSD, DragonFly BSD, and macOS. Kqueue was originally authored in 2000 by Jonathan Lemon, then involved with the FreeBSD Core Team. Kqueue makes it possible for software like nginx to solve the c10k problem.

epoll is a Linux kernel system call for a scalable I/O event notification mechanism, first introduced in version 2.5.44 of the Linux kernel. Its function is to monitor multiple file descriptors to see whether I/O is possible on any of them. It is meant to replace the older POSIX select(2) and poll(2) system calls, to achieve better performance in more demanding applications, where the number of watched file descriptors is large (unlike the older system calls, which operate in O(n) time, epoll operates in O(1) time).

References

  1. "Linux_2_6_16 - Linux Kernel Newbies". kernelnewbies.org. Retrieved 2021-03-03.
  2. D. J. Bernstein. "The self-pipe trick".
  3. BUGS, pselect(2) : synchronous I/O multiplexing   Linux Programmer's Manual – System Calls
  4. GetMessage() function with message priority list.