Nir Soffer has uploaded a new change for review. Change subject: vdsm: add eventfd and EventFile synchronization ......................................................................
vdsm: add eventfd and EventFile synchronization Change-Id: I0d237f13c42b1f4505c90d30c6d3c3ecbd1e9fa7 Signed-off-by: Federico Simoncelli <fsimo...@redhat.com> --- M lib/vdsm/Makefile.am A lib/vdsm/eventfd.py M tests/Makefile.am A tests/eventfdTests.py 4 files changed, 251 insertions(+), 0 deletions(-) git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/87/33687/1 diff --git a/lib/vdsm/Makefile.am b/lib/vdsm/Makefile.am index 4bebf28..e712cad 100644 --- a/lib/vdsm/Makefile.am +++ b/lib/vdsm/Makefile.am @@ -25,6 +25,7 @@ __init__.py \ compat.py \ define.py \ + eventfd.py \ exception.py \ ipwrapper.py \ libvirtconnection.py \ diff --git a/lib/vdsm/eventfd.py b/lib/vdsm/eventfd.py new file mode 100644 index 0000000..b2a7084 --- /dev/null +++ b/lib/vdsm/eventfd.py @@ -0,0 +1,140 @@ +# +# Copyright 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Refer to the README and COPYING files for full details of the license +# + +"""\ +This module provides the support for eventfd(2). + +More information about eventfd and usage examples can be found in the +eventfd(2) man page. + +The EventFile class provides a single synchronization object exposing +the python Event interface and associated eventfds. + +The eventfd() context manager returns a file descriptor that can be +used to provide the event notice to select, poll and epoll, e.g. + + import os + import sys + import select + import threading + import time + from vdsm.eventfd import EventFile, DATASIZE + + e = EventFile() + p = select.epoll() + + threading.Timer(5, e.set).start() + + with e.eventfd() as efd: + p.register(efd, select.EPOLLIN) + p.register(sys.stdin.fileno(), select.EPOLLIN) + + print "Echoing lines until event is received" + event_received = False + + while not event_received: + for fileno, event in p.poll(): + if not event & select.EPOLLIN: + continue + + if fileno == efd: + os.read(efd, DATASIZE) + event_received = True + elif fileno == sys.stdin.fileno(): + print os.read(sys.stdin.fileno(), 1024), + + print "Event received!" + + +The Event set() semantic is preserved in the eventfd context manager: +if the event is set then the eventfd already contains the notification. +This is both to maintain the semantic and to avoid possible races as: + + if not e.is_set(): + with e.eventfd() as efd: + ... +""" + +import os +import ctypes +import threading + +from contextlib import contextmanager + +_libc = ctypes.CDLL('libc.so.6', use_errno=True) + +EFD_NONBLOCK = os.O_NONBLOCK +EFD_CLOEXEC = 02000000 # os.O_CLOEXEC in python 3.3 +EFD_SEMAPHORE = 00000001 + +DATASIZE = ctypes.sizeof(ctypes.c_ulonglong) + + +def eventfd(initval, flags): + return _libc.eventfd(initval, flags) + + +class EventFile(object): + def __init__(self, event=None): + self.__lock = threading.Lock() + self.__fds = set() + self.__event = event or threading.Event() + + @staticmethod + def __fire_event(fd): + os.write(fd, ctypes.c_ulonglong(1)) + + def open_eventfd(self): + with self.__lock: + fd = eventfd(0, 0) + + self.__fds.add(fd) + + if self.__event.is_set(): + self.__fire_event(fd) + + return fd + + @contextmanager + def eventfd(self): + fd = self.open_eventfd() + + yield fd + + with self.__lock: + self.__fds.remove(fd) + os.close(fd) + + def isSet(self): + return self.__event.isSet() + + is_set = isSet + + def set(self): + with self.__lock: + self.__event.set() + for fd in self.__fds: + self.__fire_event(fd) + + def clear(self): + self.__event.clear() + + def wait(self, timeout=None, balancing=True): + self.__event.wait(timeout) diff --git a/tests/Makefile.am b/tests/Makefile.am index 449d7b1..120712e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -31,6 +31,7 @@ clientifTests.py \ configNetworkTests.py \ domainMonitorTests.py \ + eventfdTests.py \ fileVolumeTests.py \ fileUtilTests.py \ fuserTests.py \ diff --git a/tests/eventfdTests.py b/tests/eventfdTests.py new file mode 100644 index 0000000..be15248 --- /dev/null +++ b/tests/eventfdTests.py @@ -0,0 +1,109 @@ +# +# Copyright 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Refer to the README and COPYING files for full details of the license +# + +import os +import select +from vdsm.eventfd import EventFile, DATASIZE +from nose.tools import timed, raises, TimeExpired + +TEST_TIMEOUT = 1 +WAIT_TIMEOUT = 2 + + +def test_set(): + e = EventFile() + e.set() + assert e.is_set() + assert e.isSet() + + +def text_clear(): + e = EventFile() + e.set() + assert e.is_set() + e.clear() + assert not e.is_set() + + +@timed(TEST_TIMEOUT) +def test_wait_set(): + e = EventFile() + e.set() + e.wait(WAIT_TIMEOUT) + + +@raises(TimeExpired) +@timed(TEST_TIMEOUT) +def test_wait_noset(): + e = EventFile() + e.wait(WAIT_TIMEOUT) + + +@timed(TEST_TIMEOUT) +def test_eventfd_earlyset(): + e = EventFile() + e.set() + with e.eventfd() as fd: + assert len(__select_and_read(fd)) == DATASIZE + + +@timed(TEST_TIMEOUT) +def test_eventfd_lateset(): + e = EventFile() + with e.eventfd() as fd: + e.set() + assert len(__select_and_read(fd)) == DATASIZE + + +@raises(TimeExpired) +@timed(TEST_TIMEOUT) +def test_eventfd_noset(): + e = EventFile() + with e.eventfd() as fd: + assert len(__select_and_read(fd)) != DATASIZE + + +@timed(TEST_TIMEOUT) +def test_eventfd_multiple(): + e = EventFile() + e.set() + with e.eventfd() as fd1: + assert len(__select_and_read(fd1)) == DATASIZE + with e.eventfd() as fd2: + assert len(__select_and_read(fd2)) == DATASIZE + with e.eventfd() as fd3: + assert len(__select_and_read(fd3)) == DATASIZE + + +@raises(TimeExpired) +@timed(TEST_TIMEOUT) +def test_eventfd_clear(): + e = EventFile() + e.set() + e.clear() + with e.eventfd() as fd: + assert len(__select_and_read(fd)) != DATASIZE + + +def __select_and_read(fd): + rd, wr, ex = select.select((fd,), (), (), WAIT_TIMEOUT) + if fd in rd: + return os.read(fd, DATASIZE) + return '' -- To view, visit https://gerrit.ovirt.org/33687 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I0d237f13c42b1f4505c90d30c6d3c3ecbd1e9fa7 Gerrit-PatchSet: 1 Gerrit-Project: vdsm Gerrit-Branch: master Gerrit-Owner: Federico Simoncelli <fsimo...@redhat.com> Gerrit-Reviewer: Dan Kenigsberg <dan...@redhat.com> Gerrit-Reviewer: Federico Simoncelli <fsimo...@redhat.com> Gerrit-Reviewer: Francesco Romani <from...@redhat.com> Gerrit-Reviewer: Jenkins CI Gerrit-Reviewer: Jenkins CI RO Gerrit-Reviewer: Nir Soffer <nsof...@redhat.com> Gerrit-Reviewer: Saggi Mizrahi <smizr...@redhat.com> Gerrit-Reviewer: gerrit-hooks <automat...@ovirt.org> _______________________________________________ vdsm-patches mailing list vdsm-patches@lists.fedorahosted.org https://lists.fedorahosted.org/admin/lists/vdsm-patches@lists.fedorahosted.org