├── LICENSE.txt ├── README.md ├── scheduler.py ├── setup.py └── timerstack.c /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 rui fengyun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # timerstack 2 | 3 | ### 介绍: 4 | 5 | Python基于linux timerfd实现的定时器模块. 使用Epoll来监听调度timerfd. [更多介绍](http://xiaorui.cc) 6 | 7 | `需要说明的是, 社区中python timerfd 存在一些bug, 不可用呀... timerstack.c 是我用PyObject timerfd c api封装的. 我这边修复并加入Epoll监听事件重新打包一个可用的项目.` 8 | 9 | 具体文档可直接参考timerfd api. [linux timerfd ](http://man7.org/linux/man-pages/man2/timerfd_create.2.html) 10 | 11 | ### Install: 12 | 13 | `pip` 14 | ``` 15 | pip install timerstack 16 | ``` 17 | 18 | `source` 19 | ``` 20 | git clone https://github.com/rfyiamcool/timerstack.git 21 | cd timerstack 22 | python setup.py install 23 | ``` 24 | 25 | ### Simple Method: 26 | 27 | timerstack.create(): 创建一个相对时间的定时器fd 28 | timerstack.settime(): 设置新旧时间,可以简单理解为间隔时间和次数. 29 | timerstack.gettime(): 查看模式 30 | 31 | ``` 32 | import timerstack,os 33 | f = timerstack.create(timerfd.CLOCK_REALTIME,0) 34 | timerstack.settime(f,0,10,0) #单次 10s 35 | timerstack.settime(f,0,0,0) #停止 36 | timerstack.settime(f,0,5,5) #每5秒钟轮一次,次数不限制 37 | os.read(f,1024) 38 | ``` 39 | 40 | ### Test: 41 | ``` 42 | python scheduler.py 43 | ``` 44 | 45 | 46 | ### TO DO: 47 | more... 48 | -------------------------------------------------------------------------------- /scheduler.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import os 3 | import time 4 | import logging 5 | import select 6 | import functools 7 | 8 | import timerstack 9 | 10 | 11 | logging.basicConfig(level=logging.INFO) 12 | logger = logging.getLogger(__name__) 13 | 14 | func_map = {} 15 | 16 | 17 | def go(*args): 18 | logger.info(args) 19 | 20 | 21 | def test(): 22 | for i in range(100000): 23 | i += 10 24 | f = timerstack.create(timerstack.CLOCK_REALTIME,0) 25 | timerstack.settime(f,0,i,i) 26 | func_map[f] = functools.partial(go, i) 27 | 28 | f = timerstack.create(timerstack.CLOCK_REALTIME,0) 29 | timerstack.settime(f,0,10,0) #only once 30 | func_map[f] = functools.partial(go, i) 31 | run(func_map.keys()) 32 | 33 | 34 | def run(inputs): 35 | outputs = [] 36 | try: 37 | # 创建 epoll 句柄 38 | epoll_fd = select.epoll() 39 | # 向 epoll 句柄中注册 监听 socket 的 可读 事件 40 | for ff in inputs: 41 | epoll_fd.register(ff, select.EPOLLIN) 42 | except select.error, msg: 43 | logger.error(msg) 44 | 45 | while True: 46 | epoll_list = epoll_fd.poll() 47 | for fd, events in epoll_list: 48 | func_map[fd]() 49 | 50 | 51 | def io_select(): 52 | while inputs: 53 | readable , writable , exceptional = select.select(inputs, outputs, inputs, 0.1) 54 | for s in readable: 55 | os.read(s,1024) 56 | func_map[s](123) 57 | 58 | 59 | if __name__ == "__main__": 60 | test() 61 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup, Extension 4 | 5 | timerfdmodule = Extension("timerstack", sources = ["timerstack.c"]) 6 | 7 | longdescription = """" python use timerfd to timer engine with epoll""" 8 | 9 | setup( 10 | name = "timerstack", 11 | version = "1.0", 12 | description = "make python use timerfd_* syscalls", 13 | long_description = longdescription, 14 | author = "fengyun", 15 | author_email = "rfyiamcool@163.com", 16 | url = "http://xiaorui.cc", 17 | platforms = "Linux", 18 | ext_modules = [timerfdmodule] 19 | ) 20 | -------------------------------------------------------------------------------- /timerstack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | /* Python: create(clockid,flags) -> fd 7 | C: int timerfd_create(int clockid, int flags); */ 8 | static PyObject * _create(PyObject *self, PyObject *args) { 9 | /* variable definitions */ 10 | int clockid; 11 | int flags; 12 | int result; 13 | 14 | /* parse the function's arguments: int clockid, int flags */ 15 | if (!PyArg_ParseTuple(args, "ii", &clockid, &flags)) return NULL; 16 | 17 | /* call timerfd_create; catch errors by raising an exception */ 18 | result = timerfd_create(clockid, flags); 19 | if(result == -1) 20 | return PyErr_SetFromErrno(PyExc_OSError); 21 | 22 | /* everything's fine, return file descriptor returned by timerfd_create() */ 23 | return PyLong_FromLong(result); 24 | }; 25 | 26 | /* Python: settime(fd,flags,value,interval) -> value,interval 27 | C: int timerfd_settime(int fd, int flags, 28 | const struct itimerspec *new_value, 29 | struct itimerspec *old_value); */ 30 | static PyObject * _settime(PyObject *self, PyObject *args) { 31 | /* variable definitions */ 32 | int fd; 33 | int flags; 34 | int result; 35 | float value; 36 | float interval; 37 | struct itimerspec old_value; 38 | struct itimerspec new_value; 39 | PyObject *resulttuple; 40 | 41 | /* parse the function's arguments: int clockid, int flags */ 42 | if (!PyArg_ParseTuple(args, "iiff", &fd, &flags, &value, &interval)) 43 | return NULL; 44 | 45 | /* prepare struct itimerspec */ 46 | new_value.it_value.tv_sec = (time_t)value; 47 | new_value.it_value.tv_nsec = (long int)( 1e9 * (value - (int)value) ); 48 | 49 | new_value.it_interval.tv_sec = (time_t)interval; 50 | new_value.it_interval.tv_nsec = (long int)( 1e9 * (interval - (int)interval) ); 51 | 52 | /* call timerfd_settime; catch errors by raising an exception */ 53 | result = timerfd_settime(fd, flags, &new_value, &old_value); 54 | if (result == -1) 55 | return PyErr_SetFromErrno(PyExc_OSError); 56 | 57 | /* convert returned struct old_value */ 58 | value = (float)old_value.it_value.tv_sec + (float)old_value.it_value.tv_nsec / 1e9; 59 | interval = (float)old_value.it_interval.tv_sec + (float)old_value.it_interval.tv_nsec / 1e9; 60 | resulttuple = Py_BuildValue("(ff)", value, interval); 61 | 62 | /* everything's fine, return tuple (value,interval) created from old_value */ 63 | return resulttuple; 64 | }; 65 | 66 | 67 | /* Python: gettime(fd) -> value,interval 68 | C: int timerfd_gettime(int fd, struct itimerspec *curr_value); */ 69 | static PyObject * _gettime(PyObject *self, PyObject *args) { 70 | /* variable definitions */ 71 | int fd; 72 | int flags; 73 | int result; 74 | float value; 75 | float interval; 76 | struct itimerspec curr_value; 77 | PyObject *resulttuple; 78 | 79 | /* parse the function's arguments: int clockid, int flags */ 80 | if (!PyArg_ParseTuple(args, "i", &fd)) 81 | return NULL; 82 | 83 | /* call timerfd_gettime; catch errors by raising an exception */ 84 | result = timerfd_gettime(fd, &curr_value); 85 | if(result == -1) 86 | return PyErr_SetFromErrno(PyExc_OSError); 87 | 88 | /* convert returned struct old_value */ 89 | value = (float)curr_value.it_value.tv_sec + (float)curr_value.it_value.tv_nsec / 1e9; 90 | interval = (float)curr_value.it_interval.tv_sec + (float)curr_value.it_interval.tv_nsec / 1e9; 91 | resulttuple = Py_BuildValue("(ff)", value, interval); 92 | 93 | /* everything's fine, return tuple (value,interval) created from old_value */ 94 | return resulttuple; 95 | }; 96 | 97 | 98 | static PyMethodDef methods[] = { 99 | {"create", _create, METH_VARARGS, 100 | "create(clockid,flags) -> file descriptor\n\n" 101 | "Create a new timer object and return a file descriptor that refers to that timer.\n\n" 102 | "The 'clockid' argument specifies the clock that is used to mark the progress of\n" 103 | "the timer, and must be either CLOCK_REALTIME or CLOCK_MONOTONIC.\n\n" 104 | "CLOCK_REALTIME is a settable system-wide clock. CLOCK_MONOTONIC is a nonsettable\n" 105 | "clock that is not affected by discontinuous changes in the system clock\n" 106 | "(e.g., manual changes to system time).\n\n" 107 | "Starting with Linux 2.6.27, the following values may be bitwise ORed in flags to\n" 108 | "change the behavior of timerfd_create():\n\n" 109 | " TFD_NONBLOCK Set the O_NONBLOCK file status flag on the new open file\n" 110 | " description. Using this flag saves extra calls to 'fcntl()' to\n" 111 | " achieve the same result.\n\n" 112 | " TFD_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file\n" 113 | " descriptor. See the description of the O_CLOEXEC flag in\n" 114 | " 'open()' for reasons why this may be useful.\n\n" 115 | "An OSError exception will be raised if an error occurs. The following error\n" 116 | "numbers (refer to module 'errno') might occur:\n\n" 117 | " EINVAL Either the 'clockid' argument is neither CLOCK_MONOTONIC nor\n" 118 | " CLOCK_REALTIME; or 'flags' is invalid; or, in Linux 2.6.26 or\n" 119 | " earlier, 'flags' is nonzero.\n\n" 120 | " EMFILE Either the per-process limit of open file descriptors or the system-\n" 121 | " wide limit on the total number of open files has been reached.\n\n" 122 | " ENODEV Could not mount (internal) anonymous inode device.\n\n" 123 | " ENOMEM There was insufficient kernel memory to create the timer.\n" 124 | }, 125 | {"settime", _settime, METH_VARARGS, 126 | "settime(fd,flags,value,interval) -> old_value,old_interval\n\n" 127 | "Arm (start) or disarm (stop) the timer referred to by the file descriptor fd\n" 128 | "and return the previous settings.\n\n" 129 | "The 'value' argument specifies the initial expiration of the timer, in seconds\n" 130 | "(as a float). A value of zero disarms the timer.\n\n" 131 | "Setting the 'interval' argument to nonzero float values specifies the period,\n" 132 | "in seconds, for repeated timer expirations after the initial expiration. If\n" 133 | "'interval' is set to zero, the timer expires just once, at the time specified\n" 134 | "by 'value'.\n\n" 135 | "The 'flags' argument is either zero, to start a relative timer or\n" 136 | "TFD_TIMER_ABSTIME, to start an absolute timer. While a relative timer will\n" 137 | "expire after an amount of time specified in 'value', an absolute timer will\n" 138 | "expire if the internal clock reaches the value specified by 'value'\n" 139 | "(i.e. seconds since the epoch).\n\n" 140 | "An OSError exception will be raised if an error occurs. The following error\n" 141 | "numbers (refer to module 'errno') might occur:\n\n" 142 | " EBADF fd is not a valid file descriptor.\n\n" 143 | " EINVAL Either fd is not a valid timer file descriptor or flags is invalid.\n" 144 | }, 145 | {"gettime", _gettime, METH_VARARGS, 146 | "gettime(fd) -> value,interval\n\n" 147 | "Return the current setting of the timer referred to by the file descriptor 'fd'.\n\n" 148 | "The 'value' variable returns the amount of time until the timer will expire.\n" 149 | "If it is zero, then the timer is currently disarmed. This variable always\n" 150 | "contains a relative value, regardless of whether the TFD_TIMER_ABSTIME flag was\n" 151 | "specified when setting the timer.\n\n" 152 | "The 'interval' variable returns the interval of the timer. If it is zero, then\n" 153 | "the timer is set to expire just once, at the time specified by 'value'.\n\n" 154 | "An OSError exception will be raised if an error occurs. The following error\n" 155 | "numbers (refer to module 'errno') might occur:\n\n" 156 | " EBADF fd is not a valid file descriptor.\n\n" 157 | " EINVAL fd is not a valid timerfd file descriptor.\n" 158 | 159 | }, 160 | {NULL, NULL, 0, NULL} 161 | }; 162 | 163 | #if PY_MAJOR_VERSION >= 3 164 | static struct PyModuleDef timerfdmodule = { 165 | PyModuleDef_HEAD_INIT, 166 | "timerfd", 167 | "Python bindings for the Linux system calls 'timerfd_create()',\n" 168 | "'timerfd_settime()' and 'timerfd_gettime()'. These system calls create/manage\n" 169 | "timers that notify via file descriptors (available since kernel 2.6.25).\n\n" 170 | "Reading such a timer file returns an unsigned 8-byte integer containing the\n" 171 | "number of expirations that have occurred. As of Python 3.2 the returned bytes\n" 172 | "can be converted to a Python int using the 'from_bytes()' method:\n\n" 173 | " filecontent = os.read(fd,1024)\n" 174 | " expirations = int.from_bytes(filecontent,byteorder=sys.byteorder)\n\n" 175 | "Of course these timers can be monitored with 'select()' or 'epoll()'.\n\n" 176 | "Feel free to consult the manpage of timerfd_create for further information.\n" 177 | "(this documentation is based on the original manpage of 'timerfd_create()')\n", 178 | -1, 179 | methods 180 | }; 181 | #endif 182 | 183 | #if PY_MAJOR_VERSION >= 3 184 | PyMODINIT_FUNC PyInit_timerfd(void) { 185 | #else 186 | void inittimerstack(void) { 187 | //void inittimerfd(void) { 188 | #endif 189 | PyObject *m; 190 | #if PY_MAJOR_VERSION >= 3 191 | m = PyModule_Create(&timerfdmodule); 192 | #else 193 | m = Py_InitModule("timerstack",methods); 194 | #endif 195 | if (m != NULL) { 196 | /* define timerfd constants */ 197 | PyModule_AddIntConstant(m, "CLOCK_REALTIME", CLOCK_REALTIME); 198 | PyModule_AddIntConstant(m, "CLOCK_MONOTONIC", CLOCK_MONOTONIC); 199 | PyModule_AddIntConstant(m, "TFD_CLOEXEC", TFD_CLOEXEC); 200 | PyModule_AddIntConstant(m, "TFD_NONBLOCK", TFD_NONBLOCK); 201 | PyModule_AddIntConstant(m, "TFD_TIMER_ABSTIME", TFD_TIMER_ABSTIME); 202 | } 203 | #if PY_MAJOR_VERSION >= 3 204 | return m; 205 | #endif 206 | } 207 | 208 | --------------------------------------------------------------------------------