46 Commits
Author SHA1 Message Date
Joachim Nilsson 5065440f52 Minor change libuev --> libuEv and bump version for release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 01:18:07 +01:00
Joachim Nilsson 8e1f384c38 README: Minor cleanup, after comments by @vonj
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-06 22:50:09 +01:00
Joachim Nilsson 3094ef2887 README.md: Last update, possibly. Mostly tweaks and cleanup.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-31 02:19:20 +01:00
Joachim Nilsson e50f8aee94 Yet another API section update.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:22:05 +01:00
Joachim Nilsson 0bf22de8e0 More API section updates.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:13:34 +01:00
Joachim Nilsson 78d140f310 Cleanup API.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:08:38 +01:00
Joachim Nilsson e681657ca7 Syntax highlight API on GitHub
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:04:02 +01:00
Joachim Nilsson e707d24290 Update README on the uev_run() flags parameter and slight fixes.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:02:45 +01:00
Joachim Nilsson 7f10528d80 main.c:uev_run() - Document mysterious flags parameter.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 20:49:08 +01:00
Joachim Nilsson d352e176dc Minor spell check fixes and additions.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 19:53:56 +01:00
Joachim Nilsson 93d9d06802 First cleanup of README after comments by @vonj, more to come ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 19:36:18 +01:00
Joachim Nilsson 23935c248a Add joystick reading example, for documentation.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 18:07:22 +01:00
Joachim Nilsson 59afe57995 Minor update to README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-28 21:54:40 +01:00
Joachim Nilsson 1dfdc875d7 Update README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-28 21:36:01 +01:00
Joachim Nilsson 3d93de3806 Update REAMDE 2013-08-06 14:12:54 +02:00
Joachim Nilsson 70881326d0 Minor fixes to bench.c when running many descriptors.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:54:09 +02:00
Joachim Nilsson 1b34da7a1a Update REAMDE 2013-08-06 02:15:41 +02:00
Joachim Nilsson 0de9cb6321 Adapt bench.c (from libev) to libuev, no libev/libevent API wrappers available
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:04:41 +02:00
Joachim Nilsson d9f078a119 Reindent bench.c to Linux coding style
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:04:41 +02:00
Joachim Nilsson f7e93f9d43 Import Niels Provos bench.c, this version from http://libev.schmorp.de/bench.c
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:03:33 +02:00
Joachim Nilsson 82a09196f0 Add support for make check using Sparse
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson 66c5606e34 Add support for UEV_ONCE and UEV_NONBLOCK run flags to uev_run()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson d6e0146803 Major refactor: API change, code cleanup, remove calloc()/free()
- Rename all _create() functions --> _init()
- Rename all _delete() functions --> _stop()
- Watchers are automatically started, except
- Timer watchers are automatically started by uev_run()
- Allow I/O watchers to watch either EV_READ/EV_WRITE or both events
- Extend callbacks with a received events field
- Remove calloc()/free(), let user handle this
- Make event has-a context when initializing it => simplifies API
- Internal uev_watcher_*() API mostly handles epoll stop/start

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson c69ce87eb8 Rename uev_t --> uev_ctx_t and uev_watcher_t --> uev_t
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson 689b407c8f Update .gitignore, improved filtering *.o, .*.d, *~ 2013-08-06 02:00:24 +02:00
Joachim Nilsson baa02de05f Makefile: Add dist target. 2013-07-23 13:28:49 +02:00
Joachim Nilsson 1319f2ba5a Add support for signals using signalfd(2)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 03:19:13 +02:00
Joachim Nilsson b1de765841 Further cleanup, rename uev_io_t --> uev_watcher_t
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 02:25:51 +02:00
Joachim Nilsson 85da186489 Simplify epoll handling slightly
No need to allocate the epoll events array since it's static
in size anyway.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 02:09:13 +02:00
Joachim Nilsson 9b5be13e62 Split libuev.c into smaller files to help linker
Split libuev.c into one file per watcher type, mostly to give a better
overview and ease of maintenance, but also to help the linker.

Unfortunately this steals a few bytes when linking a full blown app, but
for apps that, e.g., only need I/O watchers and no timers the resulting
binary will be smaller.  This applies to static binaries that are more
common in embedded systems.

$ size libuev.a libuev.so.1
   text	   data	    bss	    dec	    hex	filename
    999	      0	      0	    999	    3e7	main.o (ex libuev.a)
     90	      0	      0	     90	     5a	io.o (ex libuev.a)
    490	      0	      0	    490	    1ea	timer.o (ex libuev.a)
   4380	    696	      8	   5084	   13dc	libuev.so.1

Also, move uev.h back to top from include/libuev/uev.h since a separate
namespace is not needed after the removal of the OpenBSD queue.h copy.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 00:54:52 +02:00
Joachim Nilsson 069294f28a Further reduce size and remove queue.h, we can use GLIBC's old queue.h now
- Cleanup code
- Further polish on total size using black magic
- Remove OpenBSD queue.h, code simplied so much now it's not needed anymore.
  Instead we can rely on the standard GNU/Linux sys/queue.h (old BSD copy)

$ size libuev.a libuev.so.1
   text	   data	    bss	    dec	    hex	filename
   1520	      0	      0	   1520	    5f0	libuev.o (ex libuev.a)
   4194	    680	      8	   4882	   1312	libuev.so.1

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 00:07:57 +02:00
Joachim Nilsson 6ed1352404 Optimize for size a bit
- Remove double book keeping, the kernel does all the heavy lifting now
- Trim the amount of #includes
- Use LIST_ instead of TAILQ_ now that we no longer handle timers ourselves

Original LibUEvent

$ size libuevent.a libuevent.so.1
   text	   data	    bss	    dec	    hex	filename
   2610	      0	      8	   2618	    a3a	libuevent.o (ex libuevent.a)
   5266	    648	     16	   5930	   172a	libuevent.so.1

Refactored libuev:

$ size libuev.a libuev.so.1
   text	   data	    bss	    dec	    hex	filename
   1557	      0	      0	   1557	    615	libuev.o (ex libuev.a)
   4186	    672	      8	   4866	   1302	libuev.so.1

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-22 23:07:15 +02:00
Joachim Nilsson ed22e88c81 Update README 2013-07-22 20:45:10 +02:00
Joachim Nilsson bccd32a7ee Update with info on original LibUEvent author vs current libuev maintainer. 2013-07-22 20:35:05 +02:00
Joachim Nilsson ce5f29db50 Refer to Wikipedia for information about the MIT license. 2013-07-22 20:33:23 +02:00
Joachim Nilsson 499cbd2032 Refactor to use modern timerfd and epoll
This is a major refactor of the original design by Flemming Madsen.  It
converts the library timers from poll() to timerfd, which at the same
time also unifies the file and timer watchers into one design.  The idea
is to also add signalfd to the mix later on.

Also a couple of hundred kilobytes smaller, although size a secondary
goal.  The primary goal is to further simplify the internal design and
to continuously strive to employ modern APIs.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-22 20:25:08 +02:00
Joachim Nilsson 30f6eccecd Update .gitignore 2013-07-22 20:25:05 +02:00
Joachim Nilsson 49c72c0914 Create unique include file namespace and fix install/uninstall make targets
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 13:08:40 +02:00
Joachim Nilsson f590237403 Update README 2013-07-21 11:55:07 +02:00
Joachim Nilsson ec9351bfdd Add support for peridic timers and resetting timers
Refactor uev_timer_create() to support a periodic (msec) argument. For
such timers libuev automaticlly respawns them.  Effectively resetting
the timeout using the period value.

Also, refactor argument order to uev_timer_create() and uev_io_create().

Rename UEV_FHIN, UEV_FHOUT to UEV_DIR_INBOUND and UEV_DIR_OUTBOUND.

Remove unnecessary return value for watcher callbacks.  What would those
have been usefu for, ever?  Not even the original code checked the return
value after calling a handler.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 11:39:26 +02:00
Joachim Nilsson 97398e0a7d Fix Makefile dependency handling and add install/uninstall targets
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 10:20:19 +02:00
Joachim Nilsson e1b1daf5e3 Simplify linked list of I/O watchers and its garbage collect mechanism.
Simplifed uev_io_t by removing uev_io_rem_t completely.  A refactor that
would not have been possible without the BSD queue.h API.  This makes
the code readable and thus maintainable and less error prone.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 08:31:33 +02:00
Joachim Nilsson ece4e31c52 Convert from homebrew linked list to BSD TAILQ using OpenBSD r1.38 sys/queue.h
- OpenBSD http://www.openbsd.org/cgi-bin/cvsweb/src/sys/sys/queue.h rev 1.38
- Convert lList*() to TAILQ_*()
- Minor local variable rename to clarify usage

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 07:53:25 +02:00
Joachim Nilsson ab8c70d501 Slight reorder and renaming to clarify, also update README. 2013-07-20 22:48:52 +02:00
Joachim Nilsson 7e87644bd1 Major cleanup, use Linux coding style, remove CamelCase.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-19 12:41:59 +02:00
Joachim Nilsson 0bba080660 Initial import of Flemming Madsen's LibUEvent
This fork is called libuev and will focus solely on embedded Linux
systems using C.  Patches for other UNIX-like operating systems are
welcome.

- Remove Lua support, will not be used.
- Rename from LibUEvent to libuev where applicable
- Add project's MIT license to top of all files, as is customary
- Add a LICENSE file, as is customary

This MIT licensed code was cloned from the Mercurial repository at
Google http://code.google.com/p/libuevent/ which had mislabled the
license as "New BSD License", which is wrong.  The actual license
of the project is https://en.wikipedia.org/wiki/MIT_License

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-18 23:37:07 +02:00
19 changed files with 1596 additions and 1354 deletions

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
bench
test
.*.d
*.o
*~
libuev.a
libuev.so.1
+5
View File
@@ -0,0 +1,5 @@
Current maintainer of libuEv:
Joachim Nilsson <troglobit()gmail!com>
Original LibUEvent author:
Flemming Madsen <flemming!madsen()madsensoft!dk>
+24
View File
@@ -0,0 +1,24 @@
Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For an explanation and background of the MIT license, see the Wikipedia
article https://en.wikipedia.org/wiki/MIT_License
+121 -30
View File
@@ -1,47 +1,138 @@
##
# @file Makefile
# Make the micro eventlib, lua bindings and tests
# -*-Makefile-*- for libuEv
#
# $Id$
# Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
# Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
#
# (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
##
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
.PHONY: all test clean
CROSS_COMPILE=
#CROSS_COMPILE=arm-linux-uclibc-
CC=$(CROSS_COMPILE)gcc
AR=$(CROSS_COMPILE)ar
#VERSION = $(shell git tag -l | tail -1)
VERSION ?= 1.0.1
NAME = libuev
PKG = $(NAME)-$(VERSION)
ARCHIVE = $(PKG).tar.xz
INCLUDE += -I /usr/include/lua5.1/
LDFLAGS=-g
CFLAGS=-fPIC -g -Os $(INCLUDE)
CC ?= $(CROSS)gcc
AR ?= $(CROSS)ar
STRIP ?= $(CROSS)strip
CHECK ?= sparse
CFLAGS += -fPIC -Os
CPPFLAGS += -W -Wall -Werror
ARFLAGS = crus
CHECK_FLAGS = -Wcast-to-as -Wdefault-bitfield-sign -Wdo-while -Wparen-string \
-Wptr-subtraction-blows -Wreturn-void -Wshadow -Wtypesign -Wbitwise \
-std=gnu99 -D__x86_64__ -D__LP64__ -I/usr/include/x86_64-linux-gnu
JUNK = *~ *.bak *.map .*.d DEADJOE *.gdb *.elf core core.*
MAJOR = 1
LIBNAME = $(NAME)
prefix ?= /usr/local
libdir ?= $(prefix)/lib
datadir ?= $(prefix)/share/doc/$(LIBNAME)
incdir ?= $(prefix)/include
DISTFILES = README LICENSE test.c
HEADER = uev.h
all: libuevent.a libuevent.so.$(MAJOR)
lua: luevent.so
OBJS := main.o io.o timer.o signal.o
SRCS := $(OBJS:.o=.c)
DEPS := $(addprefix .,$(SRCS:.c=.d))
VER = 1
SOLIB = $(LIBNAME).so.$(VER)
SYMLIB = $(LIBNAME).so
STATICLIB = $(LIBNAME).a
TARGET = $(STATICLIB) $(SOLIB)
libuevent.a: libuevent.o
$(AR) r $@ $^
# Pattern rules
.c.o:
@printf " CC $@\n"
@$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
libuevent.so.$(MAJOR): libuevent.o
$(CC) $(LDFLAGS) -shared -o $@ $^ -Wl,-soname=libuevent
# Smart autodependecy generation via GCC -M.
.%.d: %.c
@$(SHELL) -ec "$(CC) -MM $(CFLAGS) $(CPPFLAGS) $< 2>/dev/null \
| sed 's,.*: ,$*.o $@ : ,g' > $@; \
[ -s $@ ] || rm -f $@"
luevent.so: luevent.o all
$(CC) $(LDFLAGS) -shared -o $@ luevent.o -L . -l uevent -Wl,-soname=luevent
# Build rules
all: $(TARGET)
stripped: all lua
$(CROSS_COMPILE)strip --strip-unneeded libuevent.a libuevent.so.$(MAJOR) luevent.so
$(SOLIB): Makefile $(OBJS)
@printf " LINK $@\n"
@$(CC) $(LDFLAGS) -shared -Wl,-soname=$@ -o $@ $(OBJS)
$(STATICLIB): Makefile $(OBJS)
@printf " ARCHIVE $@\n"
@$(AR) $(ARFLAGS) $@ $(OBJS)
test: all
$(CC) -g -o test -D NO_DEBUG test.c libuevent.c && ./test
install: strip
@install -d $(DESTDIR)$(libdir)
@install $(SOLIB) $(DESTDIR)$(prefix)/lib/$(SOLIB)
@install $(STATICLIB) $(DESTDIR)$(prefix)/lib/$(STATICLIB)
@ln -sf $(SOLIB) $(DESTDIR)$(prefix)/lib/$(SYMLIB)
@install -d $(DESTDIR)$(incdir)
@install -m 0644 $(HEADER) $(DESTDIR)$(incdir)/$(HEADER)
@install -d $(DESTDIR)$(datadir)
@for file in $(DISTFILES); do \
install -m 0644 $$file $(DESTDIR)$(datadir)/$$file; \
done
luatest: lua
lua test.lua
uninstall:
-@$(RM) -r $(DESTDIR)$(datadir)
-@$(RM) -r $(DESTDIR)$(incdir)
-@$(RM) $(DESTDIR)$(prefix)/lib/$(LIBNAME)*
strip: $(TARGET)
@printf " STRIP %s\n" $(TARGET)
@$(STRIP) --strip-unneeded $(TARGET)
@size $(TARGET)
test: Makefile test.o $(STATICLIB)
@printf " TEST %s\n" $(STATICLIB)
@$(CC) $(CPPFLAGS) -g -o test test.c $(STATICLIB) && ./test
bench: Makefile bench.o $(STATICLIB)
@printf " BENCH %s\n" $(STATICLIB)
@$(CC) $(CPPFLAGS) -g -o bench bench.c $(STATICLIB) && ./bench
joystick: Makefile joystick.o $(STATICLIB)
@printf " JOTST %s\n" $(STATICLIB)
@$(CC) $(CPPFLAGS) -g -o joystick joystick.c $(STATICLIB) && ./joystick
check:
@$(CHECK) $(CHECK_FLAGS) $(CPPFLAGS) $(SRCS) bench.c
clean:
rm -f libuevent.o luevent.o libuevent.so.$(MAJOR) luevent.so test
-@$(RM) $(TARGET) *.o test
distclean: clean
-@$(RM) $(DEPS) $(JUNK)
dist:
@echo "Building .xz tarball of $(PKG) in parent dir..."
git archive --format=tar --prefix=$(PKG)/ $(VERSION) | xz >../$(ARCHIVE)
@(cd ..; md5sum $(ARCHIVE) | tee $(ARCHIVE).md5)
# Include automatically generated rules
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),distclean)
-include $(DEPS)
endif
endif
.PHONY: all test clean luatest lua
-70
View File
@@ -1,70 +0,0 @@
LibUEvent - A Microscopic event library.
========================================
The libuevent library is a small event library in the style of the venerable
Xt(3) event loop.
Basically an event context is created,and handlers for timeounts and stream
events can be registered with this context.
After initial setup, the typical application will enter the event loop that
will only return at program termination.
The C API
---------
The typical application will do the following steps
1. Create the event context with lueCtxtCreate()
2. Register initial callbacks with lueAddInput, lueAddOutput and lueAddTimer
3. Enter the event loop
Lua binding
-----------
A lua binding is available that should mix well with luasocket and luaposix.
Time resolutions are in milli second integers in order to support integer-only
embedded environments
### function luevent.addFileIO(fdesc, handler, mode)
Add a handler on a stream. The handler will sustain itself GC wise.
Use *object*:clear() to unregister the handler
* fdesc The posix file descriptor "small integer".
Use the ":getfd()" method on a "normal" lua stream or socket to obtain fdesc.
* handler A function object. The handler object is supplied as the first parameter when called.
* mode Optional. Input ("r") or output ("w") event. Default is input
* return A new filehandler object
### function luevent.addTimer(msecs, handler)
Create a new timer object. Implicitly cleared when handler is called.
Typical usage:
local hdl
hdl = luevent.addTimer(50, function() hdl = nil ..... end)
......
if hdl then hdl:clear() hdl = nil end
Here *hdl* is used to keep track of whether the timer is active.
* msecs The number of milliseconds until the timer expires
* handler A function object. The handler object is supplied as the first parameter when called.
* return A new timer object
### function object:clear()
Cancel a callback registration. Renders the object ready for GC.
### function object:handler()
Obtain the handler function from a callback registration.
Building
--------
Use make:
* make all: The library
* make test: Test and showcase
* make lua: Build the lua binding
Installation
------------
Is purely manual, but would be something along the lines of:
sudo install -m 444 libuevent.so.1 /usr/lib/libuevent.so.1
sudo install -m 444 luevent.so /usr/lib/lua/5.1/luevent.so
<!-- vim: set syntax=mkd textwidth=79 nofoldenable: -->
Symlink
+1
View File
@@ -0,0 +1 @@
README.md
+231
View File
@@ -0,0 +1,231 @@
libuEv | Simple event loop for Linux
====================================
> “Why an event loop, why not use threads?”
With the advent of light-weight processes (threads) programmers these
days have a [golden hammer](http://c2.com/cgi/wiki?GoldenHammer) they
often swing without consideration. Event loops and non-blocking I/O is
often a far easier approach, as well as less error prone.
The purpose of many applications is, with a little logic sprinkled on
top, to act upon: network packets entering an interface, timeouts
expiring, mouse clicks, or other types of events. Such applications are
often very well suited to use an event loop.
Applications that need to churn massively parallel algorithms are more
suitable for running multiple (independent) threads on several CPU
cores. However, threaded applications must deal with the side effects
of concurrency, like: race conditions, deadlocks, live locks, etc.
Writing error free threaded applications is hard, debugging them can be
even harder.
Sometimes the combination of multiple threads *and* an event loop per
thread can be the best approach, but each application of course needs to
be broken down individually to find the most optimal approach. Do keep
in mind, however, that not all systems your application will run on have
multiple CPU cores -- some small embedded systems still use a single CPU
core, even though they run Linux, with multiple threads a program may
actually run slower! Always profile your program, and if possible, test
it on different architectures.
LibuEv is a simple event loop in the style of the more established
[libevent](http://libevent.org/),
[libev](http://software.schmorp.de/pkg/libev.html) and the venerable
[Xt(3)](http://unix.com/man-page/All/3x/XtDispatchEvent) event loop.
The *u* (micro) in the name refers to both the small feature set and the
small size overhead impact of the library. The primary target of libuEv
is a modern Linux system.
Experienced developers may appreciate that libuEv is built on top of
modern Linux APIs: epoll, timerfd and signalfd.
API
---
Here is the interface to libuEv. It handles three different types of
events: I/O (files, sockets, message queues, etc.), timers, and
signals.
```C
/* Callback example, arg is passed from watcher's *_init()
* w->fd holds the file descriptor, events is set by libuEv
* to indicate if any of UEV_READ and/or UEV_WRITE is ready.
*/
void callback(uev_ctx_t *ctx, uev_t *w, void *arg, int events);
/* Event loop functions, notice use of flags! */
int uev_init (uev_ctx_t *ctx);
int uev_exit (uev_ctx_t *ctx);
int uev_run (uev_ctx_t *ctx, int flags); /* UEV_NONE, UEV_ONCE, and/or UEV_NONBLOCK */
/* I/O watcher: fd is non-blocking, events is UEV_READ and/or UEV_WRITE */
int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
int uev_io_set (uev_t *w, int fd, int events);
int uev_io_stop (uev_t *w);
/* Timer watcher: timeout and period in milliseconds */
int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
int uev_timer_set (uev_t *w, int timeout, int period); /* Change timeout or period */
int uev_timer_stop (uev_t *w); /* Stop a timer */
/* Signal watcher: signo is the signal to wait for, e.g., SIGTERM */
int uev_signal_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo);
int uev_signal_set (uev_t *w, int signo); /* Change signal to wait for */
int uev_signal_stop(uev_t *w); /* Stop signal watcher */
```
To be able to set up callbacks to events the developer first need to
create an *event context*, achieved by calling `uev_init()` with a
pointer to a local `uev_ctx_t` variable.
Events are monitored by watchers in libuEV. A watcher is nothing more
than a mechnism that polls a file descriptor. Register a watcher with a
callback to the event context by passing the `uev_ctx_t` variable, along
with an `uev_t` variable to each event's `_init()` function.
When all watchers are registered call the event loop with `uev_run()`
and the argument to the event context. The `flags` parameter is slightly
mysterious, but can be used to integrate libuEv into another event loop.
With `flags` set to `UEV_ONCE` the event loop returns after having
served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK`
the event loop returns immediately if no event is available.
Summary:
1. Prepare an event context with `uev_init()`
2. Register event callbacks with `uev_io_init()`, `uev_signal_init()`
or `uev_timer_init()`
3. Enter the event loop with `uev_run()`
4. Leave the event loop with `uev_exit()`, possibly from a callback
**Note:** Make sure to use non-blocking stream I/O! Most hard to find
bugs in event driven applications are due to sockets and files being
opened in blocking mode. Be careful out there!
Example
-------
Here follows a very brief example to illustrate how one can use libuEv
to act upon joystick input.
```C
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include "uev.h"
struct js_event {
uint32_t time; /* event timestamp in milliseconds */
int16_t value; /* value */
uint8_t type; /* event type */
uint8_t number; /* axis/button number */
} e;
static void joystick_cb(uev_ctx_t *ctx, uev_t *w, void *arg, int events)
{
read (w->fd, &e, sizeof(e));
switch (e.type) {
case 1:
if (e.value) printf("Button %d pressed\n", e.number);
else printf("Button %d released\n", e.number);
break;
case 2:
printf("Joystick axis %d moved, value %d!\n", e.number, e.value);
break;
}
}
int main(void)
{
int fd = open("/dev/input/js1", O_RDONLY, O_NONBLOCK);
uev_t js1_watcher;
uev_ctx_t ctx;
if (fd < 0)
errx(errno, "Cannot find a joystick attached.");
uev_init(&ctx);
uev_io_init(&ctx, &js1_watcher, joystick_cb, NULL, fd, UEV_READ);
puts("Starting, press Ctrl-C to exit.");
return uev_run(&ctx, 0);
}
```
To compile the program, save the code as `joystick.c` and call GCC with
`gcc -o joystick joystick.c io.c timer.c signal.c main.c` from this
directory, skips using a Makefile altogether. Alternatively, call the
`Makefile` with `make joystick` from this directory.
For a more complete, and perhaps more relevant example, see the code for
the TFTP/FTP server [uftpd](https://github.com/troglobit/uftpd). It
uses libuEv as a GIT submodule.
Also, see the `bench.c` program (`make bench` from within the library)
for [reference benchmarks](http://libev.schmorp.de/bench.html) against
libevent and libev.
Build & Install
---------------
The library is built for and developed on GNU/Linux systems, so it may
use GCC and GNU Make specific extensions here and there. This is not on
purpose and patches to correct this are most welcome. Particularly
patches to support *BSD and its kqueue interface.
* `make all`: The library
* `make test`: Test and showcase
* `make install`: Honors `$prefix` and `$DESTDIR` environment variables
Size of libuEv:
$ make strip
CC main.o
CC io.o
CC timer.o
CC signal.o
ARCHIVE libuev.a
LINK libuev.so.1
STRIP libuev.a
STRIP libuev.so.1
text data bss dec hex filename
1177 0 0 1177 499 main.o (ex libuev.a)
308 0 0 308 134 io.o (ex libuev.a)
682 0 0 682 2aa timer.o (ex libuev.a)
563 0 0 563 233 signal.o (ex libuev.a)
6306 768 8 7082 1baa libuev.so.1
Origin & References
--------------------
LibuEv (this library) was originally based on
[LibUEvent](http://code.google.com/p/libuevent/) by
[Flemming Madsen](http://www.madsensoft.dk/) but has been completely
rewritten with a much clearer API. Now more similar to the famous
[libev](http://software.schmorp.de/pkg/libev.html) by Mark Lehmann.
Another small event library used for inspiration is the very small
[Picoev](https://github.com/kazuho/picoev) by
[Oku Kazuho](https://github.com/kazuho).
* http://code.google.com/p/libuevent/
* http://software.schmorp.de/pkg/libev.html
* http://libev.schmorp.de/bench.html
* http://libevent.org/
* http://developer.cybozu.co.jp/archives/kazuho/2009/08/picoev-a-tiny-e.html
* http://coderepos.org/share/browser/lang/c/picoev/
LibuEv is maintained by [Joachim Nilsson](mailto:troglobit@gmail.com) at
[GitHub](https://github.com/troglobit/libuev)
+235
View File
@@ -0,0 +1,235 @@
/*
* Copyright 2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Mon 03/10/2003 - Modified by Davide Libenzi <davidel@xmailserver.org>
*
* Added chain event propagation to improve the sensitivity of
* the measure respect to the event loop efficency.
*
* Sun 04/08/2013 - Modified by Joachim Nilsson <troglobit@gmail.com>
*
* Adaptations for libuEv, no libev/libevent API wrappers available.
* Reindent to Linux coding style
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#define USE_PIPES
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
typedef struct {
int index;
} myarg_t;
static int num_pipes, num_active, num_writes;
static int timers, count, writes, fired;
static myarg_t *args;
static int *pipes;
static uev_t *evio;
static uev_t *evto;
static void read_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *arg, int UNUSED(events))
{
int idx, widx;
u_char ch;
myarg_t *m = arg;
idx = m->index;
widx = idx + 1;
if (timers)
uev_timer_set(&evto[idx], 10000 + drand48() * 1000, 0);
count += read(w->fd, &ch, sizeof(ch));
if (writes) {
if (widx >= num_pipes)
widx -= num_pipes;
if (write(pipes[2 * widx + 1], "e", 1) < 0) {
perror("write()");
abort();
}
writes--;
fired++;
}
}
static void timer_cb(uev_ctx_t UNUSED(*ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{
/* nop */
}
static struct timeval *run_once(uev_ctx_t *ctx)
{
int *cp, i, space;
static struct timeval ta, ts, te;
gettimeofday(&ta, NULL);
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
uev_io_set(&evio[i], cp[0], UEV_READ);
if (timers)
uev_timer_set(&evto[i], 10000 + drand48() * 1000, 0);
}
uev_run(ctx, UEV_ONCE | UEV_NONBLOCK);
fired = 0;
space = num_pipes / num_active;
space = space * 2;
for (i = 0; i < num_active; i++, fired++) {
if (write(pipes[i * space + 1], "e", 1) < 0) {
perror("write()");
abort();
}
}
count = 0;
writes = num_writes;
{
int xcount = 0;
gettimeofday(&ts, NULL);
do {
uev_run(ctx, UEV_ONCE | UEV_NONBLOCK);
xcount++;
} while (count != fired);
gettimeofday(&te, NULL);
// if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count);
}
timersub(&te, &ta, &ta);
timersub(&te, &ts, &ts);
fprintf(stdout, "%8ld %8ld\n",
ta.tv_sec * 1000000L + ta.tv_usec,
ts.tv_sec * 1000000L + ts.tv_usec);
return &te;
}
int main(int argc, char **argv)
{
struct rlimit rl;
int i, c;
int *cp;
uev_ctx_t ctx;
extern char *optarg;
num_pipes = 100;
num_active = 1;
num_writes = num_pipes;
while ((c = getopt(argc, argv, "a:n:tw:")) != -1) {
switch (c) {
case 'a':
num_active = atoi(optarg);
break;
case 'n':
num_pipes = atoi(optarg);
break;
case 't':
timers = 1;
break;
case 'w':
num_writes = atoi(optarg);
break;
default:
fprintf(stderr, "Illegal argument \"%c\"\n", c);
return 1;
}
}
rl.rlim_cur = rl.rlim_max = num_pipes * 3 + 50;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
perror("setrlimit");
return 1;
}
args = calloc(num_pipes, sizeof(myarg_t));
evio = calloc(num_pipes, sizeof(uev_t));
evto = calloc(num_pipes, sizeof(uev_t));
pipes = calloc(num_pipes * 2, sizeof(int));
if (!args || !evio || !evto || !pipes) {
perror("calloc");
return 1;
}
uev_init(&ctx);
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
if (timers)
uev_timer_init(&ctx, &evto[i], timer_cb, NULL, 0, 0);
args[i].index = i;
uev_io_init(&ctx, &evio[i], read_cb, &args[i], -1, UEV_READ);
#ifdef USE_PIPES
if (pipe(cp) == -1) {
#else
if (socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1) {
#endif
perror("pipe");
exit(1);
}
}
for (i = 0; i < 2; i++)
run_once(&ctx);
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+87
View File
@@ -0,0 +1,87 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "uev.h"
/**
* Create an I/O watcher
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb I/O callback
* @param arg Optional callback argument
* @param fd File descriptor to watch, or -1 to register an empty watcher
* @param events Requested events to watch for, a mask of %UEV_READ and %UEV_WRITE
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events)
{
if (uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events))
return -1;
if (fd < 0)
return 0;
return uev_watcher_start(w);
}
/**
* Reset an I/O watcher
* @param w Pointer to an uev_t watcher
* @param fd New file descriptor to monitor
* @param events Requested events to watch for, a mask of %UEV_READ and %UEV_WRITE
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_set(uev_t *w, int fd, int events)
{
if (uev_io_stop(w))
return -1;
/* Remove from internal list */
LIST_REMOVE(w, link);
return uev_io_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, fd, events);
}
/**
* Stop an I/O watcher
* @param w Pointer to an uev_t watcher
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_stop(uev_t *w)
{
return uev_watcher_stop(w);
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+84
View File
@@ -0,0 +1,84 @@
/* Simple example of how to use libuEv with Linux joystick API
*
* Copyright (c) 2014 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
struct js_event {
uint32_t time; /* event timestamp in milliseconds */
int16_t value; /* value */
uint8_t type; /* event type */
uint8_t number; /* axis/button number */
} e;
static void joystick_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
if (read (w->fd, &e, sizeof(e)) < 0)
errx(errno, "Failed reading joystick event");
switch (e.type) {
case 1:
if (e.value) printf("Button %d pressed\n", e.number);
else printf("Button %d released\n", e.number);
break;
case 2:
printf("Joystick axis %d moved, value %d!\n", e.number, e.value);
break;
}
}
int main(void)
{
int fd = open("/dev/input/js1", O_RDONLY, O_NONBLOCK);
uev_t js1_watcher;
uev_ctx_t ctx;
if (fd < 0)
errx(errno, "Cannot find a joystick attached.");
uev_init(&ctx);
uev_io_init(&ctx, &js1_watcher, joystick_cb, NULL, fd, UEV_READ);
puts("Starting, press Ctrl-C to exit.");
return uev_run(&ctx, 0);
}
/**
* Local Variables:
* compile-command: "gcc -o joystick joystick.c io.o timer.o signal.o main.o"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
-476
View File
@@ -1,476 +0,0 @@
/**
* @file
* Micro event library libuevent.
* Asynchronous event / mainloop library.
* Not thread safe!
* The ctxt structure needs mutex protection in case of multithreaded access
* $Id:$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/times.h>
#include <fcntl.h>
#include <time.h>
#include <poll.h>
#include <errno.h>
#include <assert.h>
#include "llist.h"
#include "libuevent.h"
#ifdef DEBUG
#define Debug(f, args...) printf(":LUE: " f "\n", ##args)
#else
#define Debug(f, args...)
#endif
static clock_t clock_tick = 0;
/// Enumerate file handle direction
enum LUEFhDirectionT {LUE_FHIN = 0, LUE_FHOUT = 1};
struct LUEFileEventH;
struct LUEFileEventRem
{
LListField(struct LUEFileEventRem);
struct LUEFileEventH *ent;
};
/// File event handle
struct LUEFileEventH
{
LListField(struct LUEFileEventH); ///< Elements for linked list
struct LUEFileEventRem rem; ///< For postponed removal
int fd; ///< File descriptor
enum LUEFhDirectionT direction; ///< Direction: in or out
int pollIx; ///< This file handle index into list of file handles to poll
LUEFileHandler handler; ///< File handler callback
void *data; ///< File handler callback parameter
};
/// Timer context object
struct LUETimerH
{
LListField(struct LUETimerH); ///< Linked list elements
int dueTime; ///< Epoch timestamp
LUETimerHandler handler; ///< Timer callback
void *data; ///< Timer callback parameter
};
/**
* @cond doxygen does not like this construction
*/
typedef LList(struct LUETimerH) LUETimerHandlerDL;
typedef LList(struct LUEFileEventH) LUEFileEventDL;
typedef LList(struct LUEFileEventRem) LUEFileEventRemL;
/**
* @endcond
*/
/// Main lue context object
struct LUECtxt
{
LUEFileEventDL fileHandlers; ///< File handlers
LUETimerHandlerDL timeoutHandlers; ///< Timer handlers
LUEFileEventRemL fileRem; ///< List of file handles to be garbage collected
LUETimerHandlerDL timeRem; ///< List of timer handles to be garbage collected
clock_t baseTime; ///< Time at last timer tick
clock_t setTime; ///< Next timer tick epoch
int timeSet; ///< True if next timer epoch is set
int exiting; ///< True if the application is terminating
};
static
struct LUEFileEventH *lueAddIO(struct LUECtxt * ctxt,
int fd, LUEFileHandler handler, void *data,
enum LUEFhDirectionT direction)
{
struct LUEFileEventH *ent;
ent = (struct LUEFileEventH *) malloc(sizeof(*ent));
Debug("Add IO %d %p", fd, ent);
assert(ent);
ent->rem.ent = ent;
ent->fd = fd;
ent->direction = direction;
ent->handler = handler;
ent->data = data;
ent->pollIx = -1;
lListAppend(&ctxt->fileHandlers, ent);
return ent;
}
/**
* Add an input file handler
*/
struct LUEFileEventH *lueAddInput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data)
{
return lueAddIO(ctxt, fd, handler, data, LUE_FHIN);
}
/**
* Add an output file handler
*/
struct LUEFileEventH *lueAddOutput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data)
{
return lueAddIO(ctxt, fd, handler, data, LUE_FHOUT);
}
static
int lueRemIO(struct LUECtxt *ctxt, struct LUEFileEventH *hdl)
{
struct LUEFileEventRem *ent;
Debug("Rem IO %p", hdl);
lListForeachIn (ent, &ctxt->fileRem)
if (ent->ent == hdl)
return -1; // Already removed
Debug("Rem FD %d", hdl->fd);
hdl->handler = NULL;
lListAppend(&ctxt->fileRem, &(hdl->rem));
return 0;
}
/**
* Remove all handlers for an fd
*/
int lueRemFd(struct LUECtxt *ctxt, int fd)
{
struct LUEFileEventH *ent;
struct LUEFileEventRem *rem;
Debug("Remfd FD %d", ent->fd);
lListForeachIn(ent, &ctxt->fileHandlers)
{
if (ent->fd != fd)
continue;
lListForeachIn(rem, &ctxt->fileRem)
if (rem->ent == ent)
return -1; // Already removed
Debug("Remfd ent %p", ent);
ent->handler = NULL;
lListAppend(&ctxt->fileRem, &(ent->rem));
}
return 0;
}
/**
* Remove an input file handler
*/
int lueRemInput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl)
{
return lueRemIO(ctxt, hdl);
}
/**
* Remove an output file handler
*/
int lueRemOutput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl)
{
return lueRemIO(ctxt, hdl);
}
/**
* Remove a timer event
*/
int lueRemTimer(struct LUECtxt *ctxt, struct LUETimerH *hdl)
{
if (hdl)
{
struct LUETimerH *ent;
lListForeachIn (ent, &ctxt->timeRem)
if (ent == hdl)
return -1; // Already removed
Debug("Remtimer %p", hdl);
lListRemove(&ctxt->timeoutHandlers, hdl);
lListAppend(&ctxt->timeRem, hdl); // Remember to free him (at a safe time)
}
return 0;
}
/**
* Add a timer event
*/
struct LUETimerH *lueAddTimer(struct LUECtxt * ctxt, int periodMs,
LUETimerHandler handler, void *data)
{
int timeDiff;
clock_t curTime;
struct LUETimerH *nent,
*ret,
*ent;
nent = (struct LUETimerH *) malloc(sizeof(*ent));
assert(nent);
nent->dueTime = periodMs;
nent->handler = handler;
nent->data = data;
if (0 == periodMs)
{
// Zero delay is a special case: A oneshot workproc: Must go first
lListInsertFirst(&ctxt->timeoutHandlers, nent);
return nent;
}
// Adjust timers.
// Jiffie wraparound is OK since we only care about diff time
curTime = ctxt->timeSet ? ctxt->setTime : times(NULL);
timeDiff = (curTime - ctxt->baseTime) / clock_tick * 1000
+ ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick;
ret = nent;
lListForeachIn(ent, &ctxt->timeoutHandlers)
{
if (ent->dueTime > timeDiff)
ent->dueTime -= timeDiff;
else
ent->dueTime = 0;
if (nent && ent->dueTime > periodMs)
{
Debug("Timer Insert %d before %d %p", periodMs, ent->dueTime, nent);
lListInsert(&ctxt->timeoutHandlers, nent, ent);
nent = NULL;
}
}
if (nent)
{
Debug("Timer Append %d at end %p", periodMs, nent);
lListAppend(&ctxt->timeoutHandlers, nent);
}
ctxt->baseTime = curTime;
return ret;
}
/**
* Create an application context
*/
struct LUECtxt *lueCtxtCreate()
{
struct LUECtxt *lue;
lue = (struct LUECtxt *) malloc(sizeof(*lue));
assert(lue);
lListInit(&lue->fileHandlers);
lListInit(&lue->timeoutHandlers);
lListInit(&lue->fileRem);
lListInit(&lue->timeRem);
lue->baseTime = times(NULL);
lue->setTime = 0;
lue->timeSet = false;
lue->exiting = false;
if (0 == clock_tick)
clock_tick = sysconf(_SC_CLK_TCK);
return lue;
}
/**
* Destroy an application context
*/
void lueCtxtDestroy(struct LUECtxt *lue)
{
lListPurge(&lue->fileHandlers);
lListPurge(&lue->timeoutHandlers);
free(lue);
}
/**
* Drive the timeouts ourself (from now on until an increment of -1)
* This is intended for use in testing frameworks
* Caveat emptor: Currently, interactions with the scheduler (agent) API is undefined.
* Not for use in lues that use the agent scheduler
* @param ctxt LUElication context object
* @param mSecs Ms to advance. Use -1 to return to realtime
*/
void lueTimeAdvance(struct LUECtxt *ctxt, int mSecs)
{
// Obviously we are executing a callback right now (arent we always)
// The time leap is taken into account before falling back into poll() below
if (mSecs >= 0)
{
if (!ctxt->timeSet)
{
ctxt->timeSet = true;
ctxt->setTime = times(NULL) + mSecs;
}
else
{
ctxt->setTime += mSecs;
}
}
else if (mSecs == -1)
{
// Go back to realtime
ctxt->timeSet = false;
ctxt->baseTime += times(NULL) - ctxt->setTime;
ctxt->setTime = 0;
}
}
/**
* Process pending events
*/
static int _lueProcessPending(struct LUECtxt *ctxt, int immediate)
{
struct LUETimerH *timer;
int pollRet;
clock_t curTime;
int curPeriod = 0;
// Do due timers
if (lListHead(&ctxt->timeoutHandlers))
{
curTime = ctxt->timeSet ? ctxt->setTime : times(NULL);
curPeriod = (curTime - ctxt->baseTime) / clock_tick * 1000
+ ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick;
Debug("Period for timer execution %d", curPeriod);
while((timer = lListHead(&ctxt->timeoutHandlers)) &&
timer->dueTime <= curPeriod)
{
Debug("Timer execution %d", timer->dueTime);
timer->handler(ctxt, timer, timer->data);
lueRemTimer(ctxt, timer);
// baseTime changes for every lueAddTimer
curPeriod = (curTime - ctxt->baseTime) / clock_tick * 1000
+ ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick;
}
lListPurge(&ctxt->timeRem); // Reap timer zombies
}
// Calculate waiting time
if (ctxt->exiting)
curPeriod = 0;
else if ((timer = lListHead(&ctxt->timeoutHandlers)) &&
timer->dueTime > curPeriod)
{
Debug("Period from timer head %d - %d -> %d",
timer->dueTime, curPeriod, timer->dueTime - curPeriod);
curPeriod = timer->dueTime - curPeriod;
}
else if (NULL != timer)
curPeriod = 0;
else
curPeriod = -1;
// Do pipe handlers
if (lListHead(&ctxt->fileHandlers) && !ctxt->exiting)
{
struct pollfd polls[lListLength(&ctxt->fileHandlers)];
struct LUEFileEventH *ent;
int ix = 0;
lListForeachIn(ent, &ctxt->fileHandlers)
{
ent->pollIx = ix;
polls[ix].fd = ent->fd;
polls[ix].events = LUE_FHIN == ent->direction ? POLLIN : POLLOUT;
polls[ix].revents = 0;
ix++;
}
Debug("Polling %d fds in %d ms", ix, curPeriod);
pollRet = poll(polls, ix, immediate ? 0 : curPeriod);
if (pollRet < 0)
{
if (EINTR == errno)
return 0;
else if (EBADF == errno)
{
lListForeachIn(ent, &ctxt->fileHandlers)
{
if (fcntl(ent->fd, F_GETFD) == -1 && EBADF == errno)
{
ent->handler(ctxt, ent, ent->fd, ent->data);
}
}
}
else
{
// Error in poll. Cannot continue
assert(false);
return;
}
}
else if (pollRet > 0)
{
lListForeachIn(ent, &ctxt->fileHandlers)
{
if (ent->handler && ent->pollIx >= 0 && polls[ent->pollIx].revents)
{
ent->handler(ctxt, ent, ent->fd, ent->data);
}
}
}
if (lListHead(&ctxt->fileRem))
{
struct LUEFileEventRem *ent;
while (NULL != (ent = lListHead(&ctxt->fileRem)))
{
lListRemove(&ctxt->fileRem, ent);
lListRemove(&ctxt->fileHandlers, ent->ent);
free(ent->ent);
}
}
}
else if (curPeriod > 0)
{
if (!immediate)
poll(NULL, 0, curPeriod);
}
else if (curPeriod == -1)
curPeriod = -2; // Nothing more to do
return curPeriod;
}
/**
* Process pending events
*/
int lueProcessPending(struct LUECtxt *ctxt)
{
return _lueProcessPending(ctxt, 1);
}
/**
* Run the application
*/
void lueRun(struct LUECtxt *ctxt)
{
int ret;
do {
ret = _lueProcessPending(ctxt, 0);
} while (!ctxt->exiting && ret != -2);
ctxt->exiting = false;
Debug("LUE exiting - bye");
}
/**
* Terminate the application
*/
void lueTerminate(struct LUECtxt *ctxt)
{
ctxt->exiting = true;
}
// vim: set ts=8 sw=3 sts=3 et:
-75
View File
@@ -1,75 +0,0 @@
/**
* @file
* Micro event library libuevent.
* Asynchronous event / mainloop library.
* Not thread safe!
* The ctxt structure needs mutex protection in case of multithreaded access
* $Id:$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. Notice at end of file
*/
#ifndef LIBUEVENT_H
#define LIBUEVENT_H
struct LUETimerH;
struct LUEFileEventH;
struct LUECtxt;
/**
* Typedef file handler callbacks
*/
typedef int (*LUEFileHandler)(struct LUECtxt *ctxt,
struct LUEFileEventH *handle,
int fd,
void *data);
/**
* Typedef timer handler callbacks
*/
typedef int (*LUETimerHandler)(struct LUECtxt *ctxt,
struct LUETimerH *handle,
void *data);
struct LUEFileEventH *lueAddInput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data);
int lueRemInput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl);
struct LUEFileEventH *lueAddOutput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data);
int lueRemOutput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl);
int lueRemFd(struct LUECtxt *ctxt, int fd);
struct LUETimerH *lueAddTimer(struct LUECtxt *ctxt, int periodMs,
LUETimerHandler handler, void *data);
int lueRemTimer(struct LUECtxt *ctxt, struct LUETimerH *hdl);
struct LUECtxt *lueCtxtCreate();
void lueCtxtDestroy(struct LUECtxt *lue);
void lueTimeAdvance(struct LUECtxt *ctxt, int mSecs);
void lueRun(struct LUECtxt *ctxt);
int lueProcessPending(struct LUECtxt *ctxt);
void lueTerminate(struct LUECtxt *ctxt);
#endif
/******************************************************************************
* Copyright (C) 2012 flemming.madsen at madsensoft.dk. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
-150
View File
@@ -1,150 +0,0 @@
/**
* @file llist.h
* (Doubly) Linked lists anchored at both ends
*
* $Id$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. Notice at end of file
*/
/// Struct mix-in. Use this inside your struct declaration
#define LListField(tn) tn * _llNext; tn * _llPrev
/// Anchor Declaration. Use like: LList(typename) varname;
#define LList(tn) struct { tn *head; tn *tail; int numElm; }
/// List init
#define lListInit(a) ((a)->head = (a)->tail = NULL, (a)->numElm = 0, (a))
/// List empty
#define lListEmpty(a) ((a)->head == NULL)
/// List head
#define lListHead(a) ((a)->head)
/// List tail
#define lListTail(a) ((a)->tail)
/// List length
#define lListLength(a) ((a)->numElm)
/// List element next
#define lListNext(e) ((e)->_llNext)
/// List element prev
#define lListPrev(e) ((e)->_llPrev)
/// Append element e to list e
#define lListAppend(a, e) \
{ \
if (lListEmpty(a)) (a)->head = (e); \
else (a)->tail->_llNext = (e); \
(e)->_llPrev = (a)->tail; \
(a)->tail = (e); \
(e)->_llNext = NULL; \
(a)->numElm++; \
}
/// Insert element n before element e in list a
#define lListInsertFirst(a, n) lListInsert(a, n, (a)->head)
/// Insert element n before element e in list a
#define lListInsert(a, n, e) \
{ \
if (!(e) || (a)->head == (e)) { \
n->_llNext = (a)->head; \
n->_llPrev = NULL; \
(a)->head = (n); \
} else { \
n->_llNext = (e); \
n->_llPrev = (e)->_llPrev; \
(e)->_llPrev->_llNext = n; \
} \
if (n->_llNext) n->_llNext->_llPrev = n; \
(a)->numElm++; \
}
/// Remove element e from list a. Caller must free element.
#define lListRemove(a, e) \
{ \
if ((a)->head) { \
if ((e) == (a)->head) (a)->head = (e)->_llNext; \
else (e)->_llPrev->_llNext = (e)->_llNext; \
if ((e) == (a)->tail) (a)->tail = (e)->_llPrev; \
else (e)->_llNext->_llPrev = (e)->_llPrev; \
(a)->numElm--; \
} \
}
/// Purge list a elements with free()
#define lListPurgeT(a, T) \
{ \
T p1, p2; \
for (p1 = (a)->head; p1; ) { \
p2 = p1; p1 = p1->_llNext; free(p2); \
} \
lListInit(a); \
}
/// Remove last element from list a
#define lListRemoveTailT(a, T) \
{ \
T p; \
if ((a)->tail) { \
p = (a)->tail; \
(a)->tail = (a)->tail->_llPrev; \
if ((a)->tail) (a)->tail->_llNext = NULL; \
else (a)->head = NULL; \
free(p); \
(a)->numElm--; \
} \
}
/// Remove first element from list a
#define lListRemoveHeadT(a, T) \
{ \
T p; \
if ((a)->head) { \
p = (a)->head; \
(a)->head = (a)->head->_llNext; \
if ((a)->head) (a)->head->_llPrev = NULL; \
else (a)->tail = NULL; \
free(p); \
(a)->numElm--; \
} \
}
// Convenience
// Only use if you have typeof() eg. gcc
#define lListPurge(a) lListPurgeT(a, typeof((a)->head))
#define lListRemoveTail(a) lListRemoveTailT(a, typeof((a)->head))
#define lListRemoveHead(a) lListRemoveHeadT(a, typeof((a)->head))
/// Traverse the list foreach e in a
#define lListForeachIn(e, a) for (e = (a)->head; e; e = e->_llNext)
/******************************************************************************
* Copyright (C) 2012 flemming.madsen at madsensoft.dk. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
// vim: set sw=3 sts=3 et:
-419
View File
@@ -1,419 +0,0 @@
/**
* @file
* Lua binding to libuevent
* $Id:$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
#include <assert.h>
#include <lua.h>
#include <lauxlib.h>
#include "libuevent.h"
static char * LUA_LTIMER_MT = "libuevent-timer";
static char * LUA_LFILEH_MT = "libuevent-fileh";
static char * LUA_LUEOBJ = "libuevent-objects";
typedef struct lue_data
{
lua_State *L; // Index to our world
struct LUETimerH *timer;
struct LUEFileEventH *file;
} lue_data;
#define toptimer(L) ((struct lue_data *)luaL_checkudata(L, 1, LUA_LTIMER_MT))
#define topfileh(L) ((struct lue_data *)luaL_checkudata(L, 1, LUA_LFILEH_MT))
static struct LUECtxt *getCtxt(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_rawgeti(L, -1, 1);
struct LUECtxt *ret = lua_touserdata(L, -1);
lua_pop(L, 2);
return ret;
}
static void l_newType(lua_State* L, const char *name)
{
luaL_newmetatable(L, name); // create metatable
lua_pushvalue(L, -1); // dup
lua_setfield(L, -2, "__index"); // metatable.__index = metatable
}
static int l_traceback (lua_State *L)
{
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback */
return 1;
}
// Top half
static int _lueHandler(lua_State *L, void *data, bool remObj)
{
lua_pushcfunction(L, l_traceback); /* push traceback function */
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, data);
lua_rawget(L, -2); // objects[object_p] => The object
lua_pushvalue(L, -1);
lua_rawget(L, -3); // object[object] => closure
if (remObj)
{
lua_pushlightuserdata(L, data);
lua_pushnil(L);
lua_rawset(L, -5); // objects[object_p] = nil -> GC
lua_pushvalue(L, -2);
lua_pushnil(L);
lua_rawset(L, -5); // objects[object] = nil -> GC
}
lua_remove(L, -3); // objects table
int ret;
lua_insert(L, -2); // exch: closure <-> obj
if ((ret = lua_pcall(L, 1, 0, -3))) // Call closure(object)
{
fprintf(stderr, "luevent: Error: %d:\n%s\n",
ret, lua_isnil(L, -1) ? "(nil)" : lua_tostring(L, -1));
//exit(1);
}
lua_pop(L, 1); // Error handler, metatable
return 0;
}
static int _ltimerHandler(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
{
lue_data *self = (lue_data *) data;
if (!self || !self->timer)
return 0;
int ret = _lueHandler(self->L, data, true);
self->timer = NULL;
return ret;
}
static int _lfileHandler(struct LUECtxt *ctxt, struct LUEFileEventH *handle, int fd, void *data)
{
lue_data *self = (lue_data *) data;
bool remObj = false;
if (!self || !self->file)
return 0;
if (fcntl(fd, F_GETFD) == -1 && EBADF == errno)
{
remObj = true;
lueRemInput(getCtxt(self->L), self->file); // Works on both types
self->file = NULL;
}
return _lueHandler(self->L, data, remObj);
}
// Bottom half
static void lueAddObj(lua_State *L, lue_data *self, int cbix)
{
self->L = L;
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, self);
lua_pushvalue(L, -3);
lua_rawset(L, -3); // objects[self_p] = obj
lua_pushvalue(L, -2);
lua_pushvalue(L, cbix);
lua_rawset(L, -3); // objects[obj] = callback
lua_pop(L, 1); // objects
}
/* LUADOC
* Add a handler on a stream.
* The handler will sustain itself GC wise. Use &lt:object>:clear() to unregister the handler
* @param fdesc The posix file descriptor "small integer".
* <br> Use the ":getfd()" method on a "normal" lua stream or socket to obtain fdesc.
* @param handler A function object. No paramaeter supplied when called.
* @param mode [Optional] Input ("r") or output ("w") event. Default is input
* @return A new filehandler object
function luevent.addFileIO(fdesc, handler, mode)
*/
static int lfileh_add(lua_State *L)
{
int fd = luaL_checkint(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
const char *mode = luaL_optstring(L, 3, "r");
lue_data *self = (lue_data *) lua_newuserdata(L, sizeof(lue_data));
struct LUEFileEventH *h = *mode == 'r' ?
lueAddInput(getCtxt(L), fd, _lfileHandler, self) :
lueAddOutput(getCtxt(L), fd, _lfileHandler, self);
if (!h) luaL_error(L, "lueAddFileIO error");
self->file = h;
luaL_getmetatable(L, LUA_LFILEH_MT);
lua_setmetatable(L, -2);
lueAddObj(L, self, 2);
return 1;
}
/* LUADOC
* Create a new timer object. Implicitly cleared when handler is called.
* @usage <pre>
* local hdl<br>
* hdl = luevent.addTimer(50, function() hdl = nil xxx end)<br>
* ......<br>
* if hdl then hdl:clear() hdl = nil end<br>
* </pre>
* Here <tt>hdl</tt> is used to keep track of whether the timer is active.
* @param msecs The number of milliseconds until the timer expires
* @param handler A function object
* @return A new timer object
function luevent.addTimer(msecs, handler)
*/
static int ltimer_add(lua_State *L)
{
int mSecs = luaL_checkint(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
lue_data *self = (lue_data *) lua_newuserdata(L, sizeof(lue_data));
struct LUETimerH *h = self ? lueAddTimer(getCtxt(L), mSecs, _ltimerHandler, self) : NULL;
if (!h)
luaL_error(L, "lueAddTimer error");
self->timer = h;
luaL_getmetatable(L, LUA_LTIMER_MT);
lua_setmetatable(L, 3);
lueAddObj(L, self, 2);
return 1;
}
static void lobj_clear(lua_State *L, lue_data *self)
{
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, self);
lua_rawget(L, -2); // objects[object_p] => The object
lua_pushnil(L);
lua_rawset(L, -3); // objects[object] = nil -> GC
lua_pushlightuserdata(L, self);
lua_pushnil(L);
lua_rawset(L, -3); // objects[object_p] = nil -> GC
lua_pop(L, 1); // pop objects
}
/* LUADOC
* Cancel a timer callback. Renders the object ready for GC.
function timer:clear()
*/
static int ltimer_clear(lua_State *L)
{
lue_data *self = toptimer(L);
if (self->timer)
{
lueRemTimer(getCtxt(L), self->timer);
self->timer = NULL;
}
lobj_clear(L, self);
return 0;
}
/* LUADOC
* Cancel a fileio callback. Renders the object ready for GC.
function fileio:clear()
*/
static int lfileh_clear(lua_State *L)
{
lue_data *self = topfileh(L);
if (self->file)
{
lueRemInput(getCtxt(L), self->file); // Works on both types
self->file = NULL;
}
lobj_clear(L, self);
return 0;
}
/* LUADOC
* Obtain the registered handler on the object
* @return The handler
function luevent_object:handler()
*/
static int luevent_handler(lua_State *L)
{
lue_data *self = lua_touserdata(L, 1);
if (self)
{
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_getfield(L, -1, "_objects");
lua_getfield(L, -2, "_callbacks");
if (lua_istable(L, -1) && lua_istable(L, -2))
{
lua_pushlightuserdata(L, self);
lua_rawget(L, -2); // The object
lua_rawget(L, -2); // callbacks[obj] -> closure
return 1;
}
}
return 0;
}
static int ltimer_gc(lua_State *L)
{
//STACKDUMP(L);
lue_data *self = toptimer(L);
if (self->timer)
{
lueRemTimer(getCtxt(L), self->timer);
self->timer = NULL;
}
return 0;
}
static int lfileh_gc(lua_State *L)
{
lue_data *self = topfileh(L);
if (self->file)
{
lueRemInput(getCtxt(L), self->file);
self->file = NULL;
}
return 0;
}
static int luevent_tostring(lua_State *L)
{
lue_data *self = lua_touserdata(L, 1);
lua_getmetatable(L, 1);
lua_getfield(L, -1, "_typename");
const char *t = lua_tostring(L, -1);
if (!self->timer && !self->file)
lua_pushfstring(L, "%s (cleared)", t);
else
lua_pushfstring(L, "%s (%p)", t, self);
return 1;
}
/* LUADOC
* Get the system time in hight resolution
* @return two numbers: seconds and milliseconds since the epoch
function luevent.systemTime()
*/
static int l_systemTime (lua_State *L)
{
struct timeval v;
gettimeofday(&v, (struct timezone *) NULL);
lua_pushinteger(L, v.tv_sec);
lua_pushinteger(L, v.tv_usec / 1000);
return 2; // num of results
}
static int l_run(lua_State * L)
{
lueRun(getCtxt(L));
return 0;
}
static int l_terminate(lua_State * L)
{
lueTerminate(getCtxt(L));
return 0;
}
static int l_cleanup(lua_State * L)
{
struct LUECtxt *ctxt = getCtxt(L);
lua_pushnil(L);
lua_setfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_gc(L, LUA_GCCOLLECT, 0); // Make sure all timers & schedules & callbacks are gone before context
lueCtxtDestroy(ctxt);
return 0;
}
static const luaL_Reg lueventlib[] = {
{"addFileIO", lfileh_add},
{"clearFileIO", lfileh_clear},
{"addTimer", ltimer_add},
{"clearTimer", ltimer_clear},
{"systemTime", l_systemTime},
{"run", l_run},
{"terminate", l_terminate},
{"__gc", l_cleanup},
{ NULL, NULL}
};
static const luaL_Reg ltimermeta[] = {
{"clear", ltimer_clear},
{"handler", luevent_handler},
{"__gc", ltimer_gc},
{"__tostring", luevent_tostring},
{ NULL, NULL}
};
static const luaL_Reg lfilehmeta[] = {
{"clear", lfileh_clear},
{"handler", luevent_handler},
{"__gc", lfileh_gc},
{"__tostring", luevent_tostring},
{ NULL, NULL}
};
int luaopen_luevent(lua_State * L)
{
struct LUECtxt *lueContext = lueCtxtCreate();
assert(lueContext);
luaL_register(L, lua_tostring(L, 1), lueventlib); // calls
lua_pushvalue(L, -1); // Dup
lua_setmetatable(L, -2); // Call GC at module cleanup.
l_newType(L, LUA_LTIMER_MT); // create metatable for timers
luaL_register(L, NULL, ltimermeta); // methods
lua_setfield(L, -2, "_meta_timer"); // easy access from test code
l_newType(L, LUA_LFILEH_MT); // create metatable for file handlers
luaL_register(L, NULL, lfilehmeta); // methods
lua_setfield(L, -2, "_meta_fileh"); // easy access from test code
lua_newtable(L); // Table[obj] -> Callback-closure
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, lueContext);// Table[1] = lueContext
lua_rawseti(L, -2, 1);
lua_setfield(L, -2, "_lue_objects");// easy access from test code
lua_pop(L, 1); // pop module
return 0;
}
// vim: set sw=3 sts=3 et:
+261
View File
@@ -0,0 +1,261 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <string.h> /* memset() */
#include <sys/epoll.h>
#include <sys/signalfd.h> /* struct signalfd_siginfo */
#include <unistd.h> /* close(), read() */
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
/* Private to libuEv, do not use directly! */
int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events)
{
if (!ctx || !w) {
errno = EINVAL;
return -1;
}
memset(w, 0, sizeof(*w));
w->ctx = ctx;
w->fd = fd;
w->type = type;
w->cb = (void *)cb;
w->arg = arg;
w->events = events;
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
return 0;
}
/* Private to libuEv, do not use directly! */
int uev_watcher_start(uev_t *w)
{
struct epoll_event ev;
if (!w || w->fd < 0) {
errno = EINVAL;
return -1;
}
if (w->active)
return 0;
ev.events = w->events;
ev.data.ptr = w;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
return -1;
w->active = 1;
return 0;
}
/* Private to libuEv, do not use directly! */
int uev_watcher_stop(uev_t *w)
{
if (!w) {
errno = EINVAL;
return -1;
}
if (!w->active)
return 0;
/* Remove from kernel */
epoll_ctl(w->ctx->fd, EPOLL_CTL_DEL, w->fd, NULL);
w->active = 0;
return 0;
}
/**
* Create an event loop context
* @param ctx Pointer to an uev_ctx_t context to be initialized
*
* @return POSIX OK(0) on success, or non-zero on error.
*/
int uev_init(uev_ctx_t *ctx)
{
int fd;
fd = epoll_create(1);
if (fd < 0)
return -1;
memset(ctx, 0, sizeof(*ctx));
ctx->fd = fd;
LIST_INIT(&ctx->watchers);
return 0;
}
/**
* Terminate the event loop
* @param ctx A valid libuEv context
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_exit(uev_ctx_t *ctx)
{
if (!ctx) {
errno = EINVAL;
return -1;
}
while (!LIST_EMPTY(&ctx->watchers)) {
uev_t *w = LIST_FIRST(&ctx->watchers);
/* Remove from internal list */
LIST_REMOVE(w, link);
if (!w->active)
continue;
switch (w->type) {
case UEV_TIMER_TYPE:
uev_timer_stop(w);
break;
case UEV_SIGNAL_TYPE:
uev_signal_stop(w);
break;
case UEV_IO_TYPE:
uev_io_stop(w);
break;
}
}
ctx->running = 0;
close(ctx->fd);
ctx->fd = -1;
return 0;
}
/**
* Start the event loop
* @param ctx A valid libuEv context
* @param flags A mask of %UEV_ONCE and %UEV_NONBLOCK, or zero
*
* With @flags set to %UEV_ONCE the event loop returns after the first
* event has been served, useful for instance to set a timeout on a file
* descriptor. If @flags also has the %UEV_NONBLOCK flag set the event
* loop will return immediately if no event is pending, useful when run
* inside another event loop.
*
* @return POSIX OK(0) upon successful termination of the event loop, or
* non-zero on error.
*/
int uev_run(uev_ctx_t *ctx, int flags)
{
int result = 0, timeout = -1;
uev_t *w;
if (!ctx || ctx->fd < 0) {
errno = EINVAL;
return -1;
}
if (flags & UEV_NONBLOCK)
timeout = 0;
/* Start the event loop */
ctx->running = 1;
/* Start all dormant timers */
LIST_FOREACH(w, &ctx->watchers, link) {
if (UEV_TIMER_TYPE == w->type)
uev_timer_set(w, w->timeout, w->period);
}
while (ctx->running) {
int i, nfds;
struct epoll_event events[UEV_MAX_EVENTS];
while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) {
if (!ctx->running)
break;
if (EINTR == errno)
continue; /* Signalled, try again */
exit:
result = -1;
ctx->running = 0;
break;
}
for (i = 0; ctx->running && i < nfds; i++) {
w = (uev_t *)events[i].data.ptr;
if (UEV_TIMER_TYPE == w->type) {
uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp))
goto exit;
if (!w->period)
w->timeout = 0;
}
if (UEV_SIGNAL_TYPE == w->type) {
struct signalfd_siginfo fdsi;
ssize_t sz = sizeof(fdsi);
if (read(w->fd, &fdsi, sz) != sz)
goto exit;
w->signo = fdsi.ssi_signo;
}
if (w->cb)
w->cb((struct uev *)ctx, w, w->arg);
if (UEV_TIMER_TYPE == w->type) {
if (!w->timeout)
uev_timer_stop(w);
}
}
if ((flags & UEV_ONCE) || LIST_EMPTY(&ctx->watchers))
break;
}
return result;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+130
View File
@@ -0,0 +1,130 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <unistd.h> /* close(), read() */
#include "uev.h"
/**
* Create a signal watcher
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb Timer callback
* @param arg Optional callback argument
* @param signo Signal to watch for
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_signal_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo)
{
int fd;
sigset_t mask;
sigemptyset(&mask);
fd = signalfd(-1, &mask, SFD_NONBLOCK);
if (fd < 0)
return -1;
if (uev_watcher_init(ctx, w, UEV_SIGNAL_TYPE, cb, arg, fd, UEV_READ))
goto exit;
if (uev_signal_set(w, signo)) {
uev_watcher_stop(w);
exit:
close(fd);
return -1;
}
return 0;
}
/**
* Reset a signal watcher
* @param w Watcher to reset
* @param signo New signal to watch for
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_signal_set(uev_t *w, int signo)
{
sigset_t mask;
/* Every watcher must be registered to a context */
if (!w || !w->ctx) {
errno = EINVAL;
return -1;
}
/* Handle stopped signal watchers */
if (w->fd < 0) {
/* Remove from internal list */
LIST_REMOVE(w, link);
if (uev_signal_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, signo))
return -1;
}
sigemptyset(&mask);
sigaddset(&mask, signo);
/* Block signals so that they aren't handled
according to their default dispositions */
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
return -1;
if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0)
return -1;
return uev_watcher_start(w);
}
/**
* Stop a signal watcher
* @param w Watcher to stop
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_signal_stop(uev_t *w)
{
if (uev_watcher_stop(w))
return -1;
close(w->fd);
w->fd = -1;
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+132 -58
View File
@@ -1,80 +1,154 @@
/**
* @file
* Micro event library libuevent.
* Test & showcase of libuevent
/* Test libuEv event library
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include "libuevent.h"
#include <stdlib.h>
#include <unistd.h> /* intptr_t */
static period;
static client_fd, server_fd, service_fd;
static struct LUETimerH *timeOut;
int readFd, writeFd;
#include "uev.h"
static timeOutHdl(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
#define UNUSED(arg) arg __attribute__ ((unused))
typedef struct {
int counter;
} my_t;
static int in, out;
static int period = 0;
static uev_t *watchdog;
static void lifetime_cb(uev_ctx_t *ctx, uev_t UNUSED(*w), void *arg, int UNUSED(events))
{
timeOut = NULL;
printf("Timeout is exceeded %p\n", data);
lueRemFd(ctxt, readFd);
lueTerminate(ctxt);
fprintf(stderr, "\nLifetime exceeded %p\n", arg);
uev_exit(ctx);
}
static appTimeout(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
/* The pipe watchdog, if it triggers we haven't received data in time. */
static void timeout_cb(uev_ctx_t *ctx, uev_t *w, void *arg, int UNUSED(events))
{
printf("Lifetime is exceeded %p\n", data);
exit(1);
watchdog = NULL;
fprintf(stderr, "\nTimeout exceeded %p\n", arg);
uev_timer_stop(w);
uev_exit(ctx);
}
static int readCb(struct LUECtxt *ctxt, struct LUEFileEventH *handle, int fd, void *data)
static void periodic_task(uev_ctx_t UNUSED(*ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{
if (timeOut) {
lueRemTimer(ctxt, timeOut);
timeOut = lueAddTimer(ctxt, 950, timeOutHdl, NULL);
}
char msg[50];
int cnt = read(readFd, msg, sizeof(msg));
//printf("READ %.*s %d\n", cnt, msg, cnt);
printf("%.*s.%d ", cnt, msg, cnt); fflush(stdout);
return 0;
fprintf(stderr, "|");
}
static writeThread(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
static void signal_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
int cnt = (int) data;
char *msg = "TESTING";
write(writeFd, msg, cnt);
//printf("WRITE %.*s %d\n", cnt, msg, cnt);
printf("%d ", cnt); fflush(stdout);
period = cnt + 5;
lueAddTimer(ctxt, period * 100, writeThread, (void *) (cnt + 1));
fprintf(stderr, w->signo == SIGINT ? "^Cv" : "^\v");
}
main()
static void pipe_read_cb(uev_ctx_t *UNUSED(ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{
struct LUECtxt *ctxt = lueCtxtCreate();
int cnt;
char msg[50];
timeOut = lueAddTimer(ctxt, 950, timeOutHdl, (void *)1);
lueAddTimer(ctxt, 5000, appTimeout, (void *)2);
/* Kick watchdog */
if (watchdog)
uev_timer_set(watchdog, 1000, 0);
lueAddTimer(ctxt, 500, writeThread, (void *) 1);
int fd[2];
pipe(fd);
readFd = fd[0];
writeFd = fd[1];
lueAddInput(ctxt, readFd, readCb, NULL);
lueRun(ctxt);
lueCtxtDestroy(ctxt);
printf("Period is %d must be 10: %s\n", period, 10 == period ? "OK" : "ERROR!");
exit(10 == period ? 0 : 1);
cnt = read(in, msg, sizeof(msg));
// fprintf(stderr, "READ %.*s %d\n", cnt, msg, cnt);
fprintf(stderr, "%.*s.%d ", cnt, msg, cnt);
}
// vim: set sw=3 sts=3 et:
static void pipe_write_cb(uev_ctx_t UNUSED(*ctx), uev_t *w, void *arg, int UNUSED(events))
{
my_t *my = arg;
char *msg = "TESTING";
if (write(out, msg, my->counter) < 0) {
perror("\nFailed writing to pipe");
return;
}
// fprintf(stderr, "WRITE %.*s %d\n", my->counter, msg, my->counter);
fprintf(stderr, "%d ", my->counter);
period = my->counter + 5;
my->counter++;
uev_timer_set(w, period * 100, 0);
}
int main(void)
{
int fd[2];
my_t my = { .counter = 1 };
uev_t timeout, wdt, periodic, writer, reader, sigint_watcher, sigquit_watcher;
uev_ctx_t ctx;
/* Work load, one timer callback writes to a pipe periodically,
* and one I/O watcher that is called every time the pipe has
* some data. */
if (pipe(fd) < 0)
return 1;
/* */
uev_init(&ctx);
/* Signal watchers, Ctrl-C => SIGINT, Ctrl-\ => SIGQUIT */
uev_signal_init(&ctx, &sigint_watcher, signal_cb, NULL, SIGINT);
uev_signal_init(&ctx, &sigquit_watcher, signal_cb, NULL, SIGQUIT);
/* Total program execution time */
uev_timer_init(&ctx, &timeout, lifetime_cb, (void *)(intptr_t)2, 4000, 0);
/* Main pipe worker and consumer */
in = fd[0];
out = fd[1];
uev_io_init(&ctx, &reader, pipe_read_cb, NULL, in, UEV_READ);
uev_timer_init(&ctx, &writer, pipe_write_cb, &my, 400, 0);
/* Watchdog for the above timer callback, if it doesn't wake up
* and write to the pipe within a given deadline it will bark. */
uev_timer_init(&ctx, &wdt, timeout_cb, (void *)(intptr_t)1, 950, 0);
watchdog = &wdt;
/* Periodic background task */
uev_timer_init(&ctx, &periodic, periodic_task, NULL, 200, 200);
/* Start event loop */
uev_run(&ctx, 0);
fprintf(stderr, "Period is %d must be 10: %s\n", period, 10 == period ? "OK" : "ERROR!");
if (10 != period)
return 1;
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
-76
View File
@@ -1,76 +0,0 @@
#!/usr/bin/env lua
--/usr/bin/env luajit
---
-- @file
-- Micro event library libuevent.
-- Test & showcase of libuevent
--
-- (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
---
require "socket"
require "luevent"
local ADDR = "127.0.0.1"
local PORT = 42555
local timer
local period
local appTimer, readHdl, writeTimer
local function timeOut()
print("Timeout is exceeded")
appTimer:clear()
readHdl:clear()
writeTimer:clear()
luevent.terminate()
end
local function writeThread(sck, cnt)
sck:send(("TESTING"):sub(1, cnt))
period = cnt + 5
writeTimer = luevent.addTimer(100 * period, function() writeThread(sck, cnt + 1) end)
end
local function main()
appTimer = luevent.addTimer(5000, function()
print("Lifetime is exceeded")
os.exit(1);
end)
local isrv = assert(socket.bind(ADDR, PORT))
luevent.addFileIO(isrv:getfd(), function(self)
local isock = isrv:accept()
isock:settimeout(0)
readHdl = luevent.addFileIO(isock:getfd(), function(self)
-- Pipe readable
if timer then
timer:clear()
timer = luevent.addTimer(950, timeOut)
end
local str, err, prt = isock:receive(50)
if not str and err == "timeout" then str = prt end
io.write(str.."."..#str.." ") io.flush()
end)
self:clear() -- This is a oneshot
end)
timer = luevent.addTimer(950, timeOut)
local osck = socket.tcp() osck:settimeout(0) osck:connect(ADDR, PORT)
luevent.addFileIO(osck:getfd(), function(self)
writeThread(osck, 1)
self:clear() -- This is a oneshot
end, "w")
luevent.run()
print "DONE"
-- Only index '1' should be there now
for k,v in pairs(luevent._lue_objects) do print(k,v) end
package.loaded.luevent = nil
collectgarbage("collect")
print(("Period is %d must be 10: %s"):format(period, 10 == period and "OK" or "ERROR!"))
os.exit(period == 10 and 0 or 1)
end
main()
-- vim: set sw=3 sts=3 et nu:
+160
View File
@@ -0,0 +1,160 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <sys/timerfd.h>
#include <unistd.h> /* close(), read() */
#include "uev.h"
static void msec2tspec(int msec, struct timespec *ts)
{
if (msec) {
ts->tv_sec = msec / 1000;
ts->tv_nsec = (msec % 1000) * 1000000;
} else {
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
}
/**
* Create and start a timer watcher
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb Callback function
* @param arg Optional callback argument
* @param timeout Timeout in milliseconds before @param cb is called
* @param period For periodic timers this is the period time that @param timeout is reset to
*
* For one-shot timers you set @param period to zero and only use @param
* timeout. For periodic timers you likely set @param timeout to either
* zero, to call it as soon as the event loop starts, or to the same
* value as @param period. When the timer expires, the @param cb
* is called, with the optional @param arg argument. A non-periodic
* timer ends its life there, while a periodic task's @param timeout is
* reset to the @param period and restarted.
*
* A timer is automatically started if the event loop is already
* running, otherwise it is kept on hold until triggered by calling
* uev_run().
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period)
{
int fd;
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (fd < 0)
return -1;
if (uev_watcher_init(ctx, w, UEV_TIMER_TYPE, cb, arg, fd, UEV_READ))
goto exit;
if (uev_timer_set(w, timeout, period)) {
uev_watcher_stop(w);
exit:
close(fd);
return -1;
}
return 0;
}
/**
* Reset a timer
* @param w Watcher to reset
* @param timeout Timeout in milliseconds before @param cb is called
* @param period For periodic timers this is the period time that @param timeout is reset to
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_set(uev_t *w, int timeout, int period)
{
/* Every watcher must be registered to a context */
if (!w || !w->ctx) {
errno = EINVAL;
return -1;
}
/* Handle stopped timers */
if (w->fd < 0) {
/* Timer already stopped */
if (!timeout && !period)
return 0;
/* Remove from internal list */
LIST_REMOVE(w, link);
if (uev_timer_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, timeout, period))
return -1;
}
w->timeout = timeout;
w->period = period;
if (w->ctx->running) {
struct itimerspec time;
msec2tspec(timeout, &time.it_value);
msec2tspec(period, &time.it_interval);
timerfd_settime(w->fd, 0, &time, NULL);
return uev_watcher_start(w);
}
return 0;
}
/**
* Stop and unregister a timer watcher
* @param w Watcher to stop
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_stop(uev_t *w)
{
if (uev_watcher_stop(w))
return -1;
/* Stop kernel timer */
uev_timer_set(w, 0, 0);
/* Close timerfd, will have to be reopened again on reset */
close(w->fd);
w->fd = -1;
return 0;
}
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+118
View File
@@ -0,0 +1,118 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef LIBUEV_UEV_H_
#define LIBUEV_UEV_H_
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/queue.h>
/* Max. number of simulateneous events */
#define UEV_MAX_EVENTS 10
/* I/O events, for signals and timers revents will always be UEV_READ */
#define UEV_NONE 0
#define UEV_READ EPOLLIN
#define UEV_WRITE EPOLLOUT
/* Run flags */
#define UEV_ONCE 1
#define UEV_NONBLOCK 2
/* I/O, timer, or signal watcher */
typedef enum {
UEV_IO_TYPE = 1,
UEV_TIMER_TYPE,
UEV_SIGNAL_TYPE,
} uev_type_t;
/* Forward declare due to dependencys, don't try this at home kids. */
struct uev;
/* Main libuEv context type */
typedef struct {
int running;
int fd; /* For epoll() */
LIST_HEAD(,uev) watchers;
} uev_ctx_t;
/* I/O event watcher */
typedef struct uev {
LIST_ENTRY(uev) link; /* For queue.h linked list */
/* Common to all watchers */
uev_ctx_t *ctx;
uev_type_t type;
int active;
int fd;
int events;
/* Watcher callback with optional argument */
void (*cb)(struct uev *, struct uev *, void *);
void *arg;
/* Timer watchers, time in milliseconds */
int timeout;
int period;
/* Signal watchers */
int signo;
} uev_t;
/* Generic callback for watchers */
typedef void (uev_cb_t)(uev_ctx_t *ctx, uev_t *w, void *arg, int events);
/* Private methods, do not use directly! */
int uev_watcher_init (uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events);
int uev_watcher_start (uev_t *w);
int uev_watcher_stop (uev_t *w);
/* Public interface */
int uev_init (uev_ctx_t *ctx);
int uev_exit (uev_ctx_t *ctx);
int uev_run (uev_ctx_t *ctx, int flags);
int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
int uev_io_set (uev_t *w, int fd, int events);
int uev_io_stop (uev_t *w);
int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
int uev_timer_set (uev_t *w, int timeout, int period);
int uev_timer_stop (uev_t *w);
int uev_signal_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo);
int uev_signal_set (uev_t *w, int signo);
int uev_signal_stop (uev_t *w);
#endif /* LIBUEV_UEV_H_ */
/**
* Local Variables:
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/