servicemp3.cpp: more simple/flexible streaming detection
[enigma2.git] / lib / base / ebase.cpp
1 #include <lib/base/ebase.h>
2
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <errno.h>
6
7 #include <lib/base/eerror.h>
8 #include <lib/base/elock.h>
9 #include <lib/gdi/grc.h>
10
11 DEFINE_REF(eSocketNotifier);
12
13 eSocketNotifier::eSocketNotifier(eMainloop *context, int fd, int requested, bool startnow): context(*context), fd(fd), state(0), requested(requested)
14 {
15         if (startnow)
16                 start();
17 }
18
19 eSocketNotifier::~eSocketNotifier()
20 {
21         stop();
22 }
23
24 void eSocketNotifier::start()
25 {
26         if (state)
27                 stop();
28
29         if (eMainloop::isValid(&context))
30         {
31                 context.addSocketNotifier(this);
32                 state=2;  // running but not in poll yet
33         }
34 }
35
36 void eSocketNotifier::stop()
37 {
38         if (state)
39         {
40                 state=0;
41                 context.removeSocketNotifier(this);
42         }
43 }
44
45 DEFINE_REF(eTimer);
46
47 void eTimer::start(long msek, bool singleShot)
48 {
49         if (bActive)
50                 stop();
51
52         if (eMainloop::isValid(&context))
53         {
54                 bActive = true;
55                 bSingleShot = singleShot;
56                 interval = msek;
57                 clock_gettime(CLOCK_MONOTONIC, &nextActivation);
58 //              eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_nsec, msek);
59                 nextActivation += (msek<0 ? 0 : msek);
60 //              eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec );
61                 context.addTimer(this);
62         }
63 }
64
65 void eTimer::startLongTimer(int seconds)
66 {
67         if (bActive)
68                 stop();
69
70         if (eMainloop::isValid(&context))
71         {
72                 bActive = bSingleShot = true;
73                 interval = 0;
74                 clock_gettime(CLOCK_MONOTONIC, &nextActivation);
75 //              eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d sec", this, nextActivation.tv_sec, nextActivation.tv_nsec, seconds);
76                 if ( seconds > 0 )
77                         nextActivation.tv_sec += seconds;
78 //              eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec );
79                 context.addTimer(this);
80         }
81 }
82
83 void eTimer::stop()
84 {
85         if (bActive)
86         {
87                 bActive=false;
88                 context.removeTimer(this);
89         }
90 }
91
92 void eTimer::changeInterval(long msek)
93 {
94         if (bActive)  // Timer is running?
95         {
96                 context.removeTimer(this);       // then stop
97                 nextActivation -= interval;  // sub old interval
98         }
99         else
100                 bActive=true; // then activate Timer
101
102         interval = msek;                                                // set new Interval
103         nextActivation += interval;             // calc nextActivation
104
105         context.addTimer(this);                         // add Timer to context TimerList
106 }
107
108 void eTimer::activate()   // Internal Funktion... called from eApplication
109 {
110         context.removeTimer(this);
111
112         if (!bSingleShot)
113         {
114                 nextActivation += interval;
115                 context.addTimer(this);
116         }
117         else
118                 bActive=false;
119
120         /*emit*/ timeout();
121 }
122
123 // mainloop
124 ePtrList<eMainloop> eMainloop::existing_loops;
125
126 bool eMainloop::isValid(eMainloop *ml)
127 {
128         return std::find(existing_loops.begin(), existing_loops.end(), ml) != existing_loops.end();
129 }
130
131 eMainloop::~eMainloop()
132 {
133         existing_loops.remove(this);
134         for (std::map<int, eSocketNotifier*>::iterator it(notifiers.begin());it != notifiers.end();++it)
135                 it->second->stop();
136         while(m_timer_list.begin() != m_timer_list.end())
137                 m_timer_list.begin()->stop();
138 }
139
140 void eMainloop::addSocketNotifier(eSocketNotifier *sn)
141 {
142         int fd = sn->getFD();
143         if (m_inActivate && m_inActivate->ref.count == 1)
144         {
145                 /*  when the current active SocketNotifier's refcount is one,
146                         then no more external references are existing.
147                         So it gets destroyed when the activate callback is finished (->AddRef() / ->Release() calls in processOneEvent).
148                         But then the sn->stop() is called to late for the next Asserion.
149                         Thus we call sn->stop() here (this implicitly calls eMainloop::removeSocketNotifier) and we don't get trouble
150                         with the next Assertion.
151                 */
152                 m_inActivate->stop();
153         }
154         ASSERT(notifiers.find(fd) == notifiers.end());
155         notifiers[fd]=sn;
156 }
157
158 void eMainloop::removeSocketNotifier(eSocketNotifier *sn)
159 {
160         int fd = sn->getFD();
161         std::map<int,eSocketNotifier*>::iterator i(notifiers.find(fd));
162         if (i != notifiers.end())
163         {
164                 notifiers.erase(i);
165                 return;
166         }
167         for (i = notifiers.begin(); i != notifiers.end(); ++i)
168                 eDebug("fd=%d, sn=%p", i->second->getFD(), (void*)i->second);
169         eFatal("removed socket notifier which is not present, fd=%d", fd);
170 }
171
172 int eMainloop::processOneEvent(unsigned int twisted_timeout, PyObject **res, ePyObject additional)
173 {
174         int return_reason = 0;
175                 /* get current time */
176
177         if (additional && !PyDict_Check(additional))
178                 eFatal("additional, but it's not dict");
179
180         if (additional && !res)
181                 eFatal("additional, but no res");
182
183         long poll_timeout = -1; /* infinite in case of empty timer list */
184
185         if (!m_timer_list.empty())
186         {
187                 /* process all timers which are ready. first remove them out of the list. */
188                 while (!m_timer_list.empty() && (poll_timeout = timeout_usec( m_timer_list.begin()->getNextActivation() ) ) <= 0 )
189                 {
190                         eTimer *tmr = m_timer_list.begin();
191                         tmr->AddRef();
192                         tmr->activate();
193                         tmr->Release();
194                 }
195                 if (poll_timeout < 0)
196                         poll_timeout = 0;
197                 else /* convert us to ms */
198                         poll_timeout /= 1000;
199         }
200
201         if ((twisted_timeout > 0) && (poll_timeout > 0) && ((unsigned int)poll_timeout > twisted_timeout))
202         {
203                 poll_timeout = twisted_timeout;
204                 return_reason = 1;
205         }
206
207         int nativecount=notifiers.size(),
208                 fdcount=nativecount,
209                 ret=0;
210
211         if (additional)
212                 fdcount += PyDict_Size(additional);
213
214                 // build the poll aray
215         pollfd pfd[fdcount];  // make new pollfd array
216         std::map<int,eSocketNotifier*>::iterator it = notifiers.begin();
217
218         int i=0;
219         for (; i < nativecount; ++i, ++it)
220         {
221                 it->second->state = 1; // running and in poll
222                 pfd[i].fd = it->first;
223                 pfd[i].events = it->second->getRequested();
224         }
225
226         if (additional)
227         {
228 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
229                 typedef int Py_ssize_t;
230 # define PY_SSIZE_T_MAX INT_MAX
231 # define PY_SSIZE_T_MIN INT_MIN
232 #endif
233                 PyObject *key, *val;
234                 Py_ssize_t pos=0;
235                 while (PyDict_Next(additional, &pos, &key, &val)) {
236                         pfd[i].fd = PyObject_AsFileDescriptor(key);
237                         pfd[i++].events = PyInt_AsLong(val);
238                 }
239         }
240
241         m_is_idle = 1;
242         ++m_idle_count;
243
244         if (this == eApp)
245         {
246                 Py_BEGIN_ALLOW_THREADS
247                 ret = ::poll(pfd, fdcount, poll_timeout);
248                 Py_END_ALLOW_THREADS
249         } else
250                 ret = ::poll(pfd, fdcount, poll_timeout);
251
252         m_is_idle = 0;
253
254                         /* ret > 0 means that there are some active poll entries. */
255         if (ret > 0)
256         {
257                 int i=0;
258                 return_reason = 0;
259                 for (; i < nativecount; ++i)
260                 {
261                         if (pfd[i].revents)
262                         {
263                                 it = notifiers.find(pfd[i].fd);
264                                 if (it != notifiers.end()
265                                         && it->second->state == 1) // added and in poll
266                                 {
267                                         m_inActivate = it->second;
268                                         int req = m_inActivate->getRequested();
269                                         if (pfd[i].revents & req) {
270                                                 m_inActivate->AddRef();
271                                                 m_inActivate->activate(pfd[i].revents & req);
272                                                 m_inActivate->Release();
273                                         }
274                                         pfd[i].revents &= ~req;
275                                         m_inActivate = 0;
276                                 }
277                                 if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
278                                         eDebug("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d)", pfd[i].fd, pfd[i].revents);
279                         }
280                 }
281                 for (; i < fdcount; ++i)
282                 {
283                         if (pfd[i].revents)
284                         {
285                                 if (!*res)
286                                         *res = PyList_New(0);
287                                 ePyObject it = PyTuple_New(2);
288                                 PyTuple_SET_ITEM(it, 0, PyInt_FromLong(pfd[i].fd));
289                                 PyTuple_SET_ITEM(it, 1, PyInt_FromLong(pfd[i].revents));
290                                 PyList_Append(*res, it);
291                                 Py_DECREF(it);
292                         }
293                 }
294         }
295         else if (ret < 0)
296         {
297                         /* when we got a signal, we get EINTR. */
298                 if (errno != EINTR)
299                         eDebug("poll made error (%m)");
300                 else
301                         return_reason = 2; /* don't assume the timeout has passed when we got a signal */
302         }
303
304         return return_reason;
305 }
306
307 void eMainloop::addTimer(eTimer* e)
308 {
309         m_timer_list.insert_in_order(e);
310 }
311
312 void eMainloop::removeTimer(eTimer* e)
313 {
314         m_timer_list.remove(e);
315 }
316
317 int eMainloop::iterate(unsigned int twisted_timeout, PyObject **res, ePyObject dict)
318 {
319         int ret = 0;
320
321         if (twisted_timeout)
322         {
323                 clock_gettime(CLOCK_MONOTONIC, &m_twisted_timer);
324                 m_twisted_timer += twisted_timeout;
325         }
326
327                 /* TODO: this code just became ugly. fix that. */
328         do
329         {
330                 if (m_interrupt_requested)
331                 {
332                         m_interrupt_requested = 0;
333                         return 0;
334                 }
335
336                 if (app_quit_now)
337                         return -1;
338
339                 int to = 0;
340                 if (twisted_timeout)
341                 {
342                         timespec now, timeout;
343                         clock_gettime(CLOCK_MONOTONIC, &now);
344                         if (m_twisted_timer<=now) // timeout
345                                 return 0;
346                         timeout = m_twisted_timer - now;
347                         to = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000;
348                 }
349                 ret = processOneEvent(to, res, dict);
350         } while ( !ret && !(res && *res) );
351
352         return ret;
353 }
354
355 int eMainloop::runLoop()
356 {
357         while (!app_quit_now)
358                 iterate();
359         return retval;
360 }
361
362 void eMainloop::reset()
363 {
364         app_quit_now=false;
365 }
366
367 PyObject *eMainloop::poll(ePyObject timeout, ePyObject dict)
368 {
369         PyObject *res=0;
370
371         if (app_quit_now)
372                 Py_RETURN_NONE;
373
374         int twisted_timeout = (timeout == Py_None) ? 0 : PyInt_AsLong(timeout);
375
376         iterate(twisted_timeout, &res, dict);
377         if (res)
378                 return res;
379
380         return PyList_New(0); /* return empty list on timeout */
381 }
382
383 void eMainloop::interruptPoll()
384 {
385         m_interrupt_requested = 1;
386 }
387
388 void eMainloop::quit(int ret)
389 {
390         retval = ret;
391         app_quit_now = true;
392 }
393
394 eApplication* eApp = 0;
395
396 #include "structmember.h"
397
398 extern "C" {
399
400 // eTimer replacement
401
402 struct eTimerPy
403 {
404         PyObject_HEAD
405         eTimer *tm;
406         PyObject *in_weakreflist; /* List of weak references */
407 };
408
409 static int
410 eTimerPy_traverse(eTimerPy *self, visitproc visit, void *arg)
411 {
412         PyObject *obj = self->tm->timeout.getSteal();
413         if (obj) {
414                 Py_VISIT(obj);
415         }
416         return 0;
417 }
418
419 static int
420 eTimerPy_clear(eTimerPy *self)
421 {
422         PyObject *obj = self->tm->timeout.getSteal(true);
423         if (obj)
424                 Py_CLEAR(obj);
425         return 0;
426 }
427
428 static void
429 eTimerPy_dealloc(eTimerPy* self)
430 {
431         if (self->in_weakreflist != NULL)
432                 PyObject_ClearWeakRefs((PyObject *) self);
433         eTimerPy_clear(self);
434         self->tm->Release();
435         self->ob_type->tp_free((PyObject*)self);
436 }
437
438 static PyObject *
439 eTimerPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
440 {
441         eTimerPy *self = (eTimerPy *)type->tp_alloc(type, 0);
442         self->tm = eTimer::create(eApp);
443         self->tm->AddRef();
444         self->in_weakreflist = NULL;
445         return (PyObject *)self;
446 }
447
448 static PyObject *
449 eTimerPy_is_active(eTimerPy* self)
450 {
451         PyObject *ret = NULL;
452         ret = self->tm->isActive() ? Py_True : Py_False;
453         Org_Py_INCREF(ret);
454         return ret;
455 }
456
457 static PyObject *
458 eTimerPy_start(eTimerPy* self, PyObject *args)
459 {
460         long v=0;
461         long singleShot=0;
462         if (PyTuple_Size(args) > 1)
463         {
464                 if (!PyArg_ParseTuple(args, "ll", &v, &singleShot)) // when 2nd arg is a value
465                 {
466                         PyObject *obj=0;
467                         if (!PyArg_ParseTuple(args, "lO", &v, &obj)) // get 2nd arg as python object
468                                 return NULL;
469                         else if (obj == Py_True)
470                                 singleShot=1;
471                         else if (obj != Py_False)
472                                 return NULL;
473                 }
474         }
475         else if (!PyArg_ParseTuple(args, "l", &v))
476                 return NULL;
477         self->tm->start(v, singleShot);
478         Py_RETURN_NONE;
479 }
480
481 static PyObject *
482 eTimerPy_start_long(eTimerPy* self, PyObject *args)
483 {
484         int v=0;
485         if (!PyArg_ParseTuple(args, "i", &v)) {
486                 return NULL;
487         }
488         self->tm->startLongTimer(v);
489         Py_RETURN_NONE;
490 }
491
492 static PyObject *
493 eTimerPy_change_interval(eTimerPy* self, PyObject *args)
494 {
495         long v=0;
496         if (!PyArg_ParseTuple(args, "l", &v)) {
497                 return NULL;
498         }
499         self->tm->changeInterval(v);
500         Py_RETURN_NONE;
501 }
502
503 static PyObject *
504 eTimerPy_stop(eTimerPy* self)
505 {
506         self->tm->stop();
507         Py_RETURN_NONE;
508 }
509
510 static PyObject *
511 eTimerPy_get_callback_list(eTimerPy *self)
512 { //used for compatibilty with the old eTimer
513         return self->tm->timeout.get();
514 }
515
516 static PyMethodDef eTimerPy_methods[] = {
517         {"isActive", (PyCFunction)eTimerPy_is_active, METH_NOARGS,
518          "returns the timer state"
519         },
520         {"start", (PyCFunction)eTimerPy_start, METH_VARARGS,
521          "start timer with interval in msecs"
522         },
523         {"startLongTimer", (PyCFunction)eTimerPy_start_long, METH_VARARGS,
524          "start timer with interval in secs"
525         },
526         {"changeInterval", (PyCFunction)eTimerPy_change_interval, METH_VARARGS,
527          "change interval of a timer (in msecs)"
528         },
529         {"stop", (PyCFunction)eTimerPy_stop, METH_NOARGS,
530          "stops the timer"
531         },
532         //used for compatibilty with the old eTimer
533         {"get", (PyCFunction)eTimerPy_get_callback_list, METH_NOARGS,
534          "get timeout callback list"
535         },
536         {NULL}  /* Sentinel */
537 };
538
539 static PyObject *
540 eTimerPy_get_cb_list(eTimerPy *self, void *closure)
541 {
542         return self->tm->timeout.get();
543 }
544
545 static PyObject *
546 eTimerPy_timeout(eTimerPy *self, void *closure) 
547 { //used for compatibilty with the old eTimer
548         Org_Py_INCREF((PyObject*)self);
549         return (PyObject*)self;
550 }
551
552 static PyGetSetDef eTimerPy_getseters[] = {
553         {"callback",
554          (getter)eTimerPy_get_cb_list, (setter)0,
555          "returns the callback python list",
556          NULL},
557
558         {"timeout", //used for compatibilty with the old eTimer
559          (getter)eTimerPy_timeout, (setter)0,
560          "synonym for our self",
561          NULL},
562
563         {NULL} /* Sentinel */
564 };
565
566 static PyTypeObject eTimerPyType = {
567         PyObject_HEAD_INIT(NULL)
568         0, /*ob_size*/
569         "eBaseImpl.eTimer", /*tp_name*/
570         sizeof(eTimerPy), /*tp_basicsize*/
571         0, /*tp_itemsize*/
572         (destructor)eTimerPy_dealloc, /*tp_dealloc*/
573         0, /*tp_print*/
574         0, /*tp_getattr*/
575         0, /*tp_setattr*/
576         0, /*tp_compare*/
577         0, /*tp_repr*/
578         0, /*tp_as_number*/
579         0, /*tp_as_sequence*/
580         0, /*tp_as_mapping*/
581         0, /*tp_hash */
582         0, /*tp_call*/
583         0, /*tp_str*/
584         0, /*tp_getattro*/
585         0, /*tp_setattro*/
586         0, /*tp_as_buffer*/
587         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
588         "eTimer objects", /* tp_doc */
589         (traverseproc)eTimerPy_traverse, /* tp_traverse */
590         (inquiry)eTimerPy_clear, /* tp_clear */
591         0, /* tp_richcompare */
592         offsetof(eTimerPy, in_weakreflist), /* tp_weaklistoffset */
593         0, /* tp_iter */
594         0, /* tp_iternext */
595         eTimerPy_methods, /* tp_methods */
596         0, /* tp_members */
597         eTimerPy_getseters, /* tp_getset */
598         0, /* tp_base */
599         0, /* tp_dict */
600         0, /* tp_descr_get */
601         0, /* tp_descr_set */
602         0, /* tp_dictoffset */
603         0, /* tp_init */
604         0, /* tp_alloc */
605         eTimerPy_new, /* tp_new */
606 };
607
608 // eSocketNotifier replacement
609
610 struct eSocketNotifierPy
611 {
612         PyObject_HEAD
613         eSocketNotifier *sn;
614         PyObject *in_weakreflist; /* List of weak references */
615 };
616
617 static int
618 eSocketNotifierPy_traverse(eSocketNotifierPy *self, visitproc visit, void *arg)
619 {
620         PyObject *obj = self->sn->activated.getSteal();
621         if (obj)
622                 Py_VISIT(obj);
623         return 0;
624 }
625
626 static int
627 eSocketNotifierPy_clear(eSocketNotifierPy *self)
628 {
629         PyObject *obj = self->sn->activated.getSteal(true);
630         if (obj)
631                 Py_CLEAR(obj);
632         return 0;
633 }
634
635 static void
636 eSocketNotifierPy_dealloc(eSocketNotifierPy* self)
637 {
638         if (self->in_weakreflist != NULL)
639                 PyObject_ClearWeakRefs((PyObject *) self);
640         eSocketNotifierPy_clear(self);
641         self->sn->Release();
642         self->ob_type->tp_free((PyObject*)self);
643 }
644
645 static PyObject *
646 eSocketNotifierPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
647 {
648         eSocketNotifierPy *self = (eSocketNotifierPy *)type->tp_alloc(type, 0);
649         int fd, req, immediate_start = 1, size = PyTuple_Size(args);
650         if (size > 2)
651         {
652                 if (!PyArg_ParseTuple(args, "iii", &fd, &req, &immediate_start))
653                 {
654                         PyObject *obj = NULL;
655                         if (!PyArg_ParseTuple(args, "iiO", &fd, &req, &immediate_start))
656                                 return NULL;
657                         if (obj == Py_False)
658                                 immediate_start = 0;
659                         else if (obj != Py_True)
660                                 return NULL;
661                 }
662         }
663         else if (size < 2 || !PyArg_ParseTuple(args, "ii", &fd, &req))
664                 return NULL;
665         self->sn = eSocketNotifier::create(eApp, fd, req, immediate_start);
666         self->sn->AddRef();
667         self->in_weakreflist = NULL;
668         return (PyObject *)self;
669 }
670
671 static PyObject *
672 eSocketNotifierPy_is_running(eSocketNotifierPy* self)
673 {
674         PyObject *ret = self->sn->isRunning() ? Py_True : Py_False;
675         Org_Py_INCREF(ret);
676         return ret;
677 }
678
679 static PyObject *
680 eSocketNotifierPy_start(eSocketNotifierPy* self)
681 {
682         self->sn->start();
683         Py_RETURN_NONE;
684 }
685
686 static PyObject *
687 eSocketNotifierPy_stop(eSocketNotifierPy* self)
688 {
689         self->sn->stop();
690         Py_RETURN_NONE;
691 }
692
693 static PyObject *
694 eSocketNotifierPy_get_fd(eSocketNotifierPy* self)
695 {
696         return PyInt_FromLong(self->sn->getFD());
697 }
698
699 static PyObject *
700 eSocketNotifierPy_get_requested(eSocketNotifierPy* self)
701 {
702         return PyInt_FromLong(self->sn->getRequested());
703 }
704
705 static PyObject *
706 eSocketNotifierPy_set_requested(eSocketNotifierPy* self, PyObject *args)
707 {
708         int req;
709         if (PyTuple_Size(args) != 1 || !PyArg_ParseTuple(args, "i", &req))
710                 return NULL;
711         self->sn->setRequested(req);
712         Py_RETURN_NONE;
713 }
714
715 static PyMethodDef eSocketNotifierPy_methods[] = {
716         {"isRunning", (PyCFunction)eSocketNotifierPy_is_running, METH_NOARGS,
717          "returns the running state"
718         },
719         {"start", (PyCFunction)eSocketNotifierPy_start, METH_NOARGS,
720          "start the sn"
721         },
722         {"stop", (PyCFunction)eSocketNotifierPy_stop, METH_NOARGS,
723          "stops the sn"
724         },
725         {"getFD", (PyCFunction)eSocketNotifierPy_get_fd, METH_NOARGS,
726          "get file descriptor"
727         },
728         {"getRequested", (PyCFunction)eSocketNotifierPy_get_requested, METH_NOARGS,
729          "get requested"
730         },
731         {"setRequested", (PyCFunction)eSocketNotifierPy_set_requested, METH_VARARGS,
732          "set requested"
733         },
734         {NULL}  /* Sentinel */
735 };
736
737 static PyObject *
738 eSocketNotifierPy_get_cb_list(eSocketNotifierPy *self, void *closure)
739 {
740         return self->sn->activated.get();
741 }
742
743 static PyGetSetDef eSocketNotifierPy_getseters[] = {
744         {"callback",
745          (getter)eSocketNotifierPy_get_cb_list, (setter)0,
746          "returns the callback python list",
747          NULL},
748         {NULL} /* Sentinel */
749 };
750
751 static PyTypeObject eSocketNotifierPyType = {
752         PyObject_HEAD_INIT(NULL)
753         0, /*ob_size*/
754         "eBaseImpl.eSocketNotifier", /*tp_name*/
755         sizeof(eSocketNotifierPy), /*tp_basicsize*/
756         0, /*tp_itemsize*/
757         (destructor)eSocketNotifierPy_dealloc, /*tp_dealloc*/
758         0, /*tp_print*/
759         0, /*tp_getattr*/
760         0, /*tp_setattr*/
761         0, /*tp_compare*/
762         0, /*tp_repr*/
763         0, /*tp_as_number*/
764         0, /*tp_as_sequence*/
765         0, /*tp_as_mapping*/
766         0, /*tp_hash */
767         0, /*tp_call*/
768         0, /*tp_str*/
769         0, /*tp_getattro*/
770         0, /*tp_setattro*/
771         0, /*tp_as_buffer*/
772         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
773         "eTimer objects", /* tp_doc */
774         (traverseproc)eSocketNotifierPy_traverse, /* tp_traverse */
775         (inquiry)eSocketNotifierPy_clear, /* tp_clear */
776         0, /* tp_richcompare */
777         offsetof(eSocketNotifierPy, in_weakreflist), /* tp_weaklistoffset */
778         0, /* tp_iter */
779         0, /* tp_iternext */
780         eSocketNotifierPy_methods, /* tp_methods */
781         0, /* tp_members */
782         eSocketNotifierPy_getseters, /* tp_getset */
783         0, /* tp_base */
784         0, /* tp_dict */
785         0, /* tp_descr_get */
786         0, /* tp_descr_set */
787         0, /* tp_dictoffset */
788         0, /* tp_init */
789         0, /* tp_alloc */
790         eSocketNotifierPy_new, /* tp_new */
791 };
792
793 static PyMethodDef module_methods[] = {
794         {NULL}  /* Sentinel */
795 };
796
797 void eBaseInit(void)
798 {
799         PyObject* m = Py_InitModule3("eBaseImpl", module_methods,
800                 "Module that implements some enigma classes with working cyclic garbage collection.");
801
802         if (m == NULL)
803                 return;
804
805         if (!PyType_Ready(&eTimerPyType))
806         {
807                 Org_Py_INCREF((PyObject*)&eTimerPyType);
808                 PyModule_AddObject(m, "eTimer", (PyObject*)&eTimerPyType);
809         }
810         if (!PyType_Ready(&eSocketNotifierPyType))
811         {
812                 Org_Py_INCREF((PyObject*)&eSocketNotifierPyType);
813                 PyModule_AddObject(m, "eSocketNotifier", (PyObject*)&eSocketNotifierPyType);
814         }
815 }
816 }