summaryrefslogtreecommitdiff
path: root/src/libwebvi/webvi
diff options
context:
space:
mode:
authorAntti Ajanki <antti.ajanki@iki.fi>2011-07-06 17:04:33 +0300
committerAntti Ajanki <antti.ajanki@iki.fi>2011-07-06 17:04:33 +0300
commitbd88072b5df85c392917e4bce11a40c0058c4657 (patch)
treec822a94e501a1f6fecf6d0742b772771f2b34441 /src/libwebvi/webvi
parent195fcc2ea284489240d58a72859f0963ad82e27a (diff)
downloadvdr-plugin-webvideo-bd88072b5df85c392917e4bce11a40c0058c4657.tar.gz
vdr-plugin-webvideo-bd88072b5df85c392917e4bce11a40c0058c4657.tar.bz2
Properly implement timeout callback (bug #619), bump SONAME because of API changes
Diffstat (limited to 'src/libwebvi/webvi')
-rw-r--r--src/libwebvi/webvi/api.py66
-rw-r--r--src/libwebvi/webvi/asyncurl.py34
-rw-r--r--src/libwebvi/webvi/constants.py7
-rw-r--r--src/libwebvi/webvi/request.py6
4 files changed, 77 insertions, 36 deletions
diff --git a/src/libwebvi/webvi/api.py b/src/libwebvi/webvi/api.py
index f7f4ad1..6dfa31e 100644
--- a/src/libwebvi/webvi/api.py
+++ b/src/libwebvi/webvi/api.py
@@ -23,29 +23,45 @@ Example workflow:
handle = new_request(ref, WebviRequestType.MENU)
-2) Setup a callback function:
+2) Setup a timeout callback:
-setopt(handle, WebviOpt.WRITEFUNC, my_callback)
+def timeout_func(t, ignored):
+ global alarm
+ now = current_time_in_milliseconds()
+ alarm = now + t
-3) Start the network transfer:
+set_config(WebviConfig.TIMEOUT_CALLBACK, timeout_func)
+
+3) Setup a data callback:
+
+set_opt(handle, WebviOpt.WRITEFUNC, write_func)
+
+4) Start the network transfer:
start_handle(handle)
-4) Get active file descriptors, wait for activity on them, and let
-webvi process the file descriptor.
+5) Get active file descriptors, wait for activity using the timeout
+value supplied by timeout callback, and let webvi process the file
+descriptor.
import select
...
readfd, writefd, excfd = fdset()[1:4]
-readfd, writefd, excfd = select.select(readfd, writefd, excfd, 5.0)
-for fd in readfd:
- perform(fd, WebviSelectBitmask.READ)
-for fd in writefd:
- perform(fd, WebviSelectBitmask.WRITE)
-
-5) Iterate 4) until pop_message returns handle, which indicates that
+timeout = alarm - current_time_in_milliseconds()
+if timeout < 0:
+ timeout = 10.0
+readfd, writefd, excfd = select.select(readfd, writefd, excfd, timeout)
+if readfd == writefd == excfd == []: # timeout
+ perform(WebviSelect.TIMEOUT, WebviSelectBitmask.CHECK)
+else:
+ for fd in readfd:
+ perform(fd, WebviSelectBitmask.READ)
+ for fd in writefd:
+ perform(fd, WebviSelectBitmask.WRITE)
+
+6) Iterate 5) until pop_message returns handle, which indicates that
the request has been completed.
finished, status, errmsg, remaining = pop_message()
@@ -105,6 +121,12 @@ def set_config(conf, value):
else:
request.DEBUG = True
return WebviErr.OK
+ elif conf == WebviConfig.TIMEOUT_CALLBACK:
+ request.set_timeout_callback(value)
+ return WebviErr.OK
+ elif conf == WebviConfig.TIMEOUT_DATA:
+ request.timeout_data = value
+ return WebviErr.OK
else:
return WebviErr.INVALID_PARAMETER
@@ -242,7 +264,7 @@ def fdset():
"""Get the list of file descriptors that are currently in use by
the library.
- Returrns a tuple, where the first item is a constants.WebviErr
+ Returns a tuple, where the first item is a constants.WebviErr
value indicating the success of the call, the next three values
are lists of descriptors that should be monitored for reading,
writing, and exceptional conditions, respectively. The last item
@@ -272,24 +294,24 @@ def perform(fd, ev_bitmask):
select() or similar system call. ev_bitmask specifies what kind of
activity has been detected using values of
constants.WebviSelectBitmask. If ev_bitmask is
- constants.WebviSelectBitmask.TIMEOUT the type of activity is check
- by the function.
+ constants.WebviSelectBitmask.CHECK the type of activity is
+ checked by the function.
- This function should be called every few seconds with fd=-1,
- ev_bitmask=constants.WebviSelectBitmask.TIMEOUT even if no
- activity has been signalled on the file descriptors to ensure
- correct handling of timeouts and other internal processing.
+ If a timeout occurs before any file descriptor becomes ready, this
+ function should be called with fd set to
+ constants.WebviSelect.TIMEOUT and ev_bitmask set to
+ constants.WebviSelectBitmask.CHECK.
"""
if fd < 0:
- asyncurl.poll()
+ asyncurl.poll_timeout()
else:
disp = socket_map.get(fd)
if disp is not None:
if ev_bitmask & WebviSelectBitmask.READ != 0 or \
- (ev_bitmask == 0 and disp.readable()):
+ (ev_bitmask == WebviSelectBitmask.CHECK and disp.readable()):
disp.handle_read_event()
if ev_bitmask & WebviSelectBitmask.WRITE != 0 or \
- (ev_bitmask == 0 and disp.writable()):
+ (ev_bitmask == WebviSelectBitmask.CHECK and disp.writable()):
disp.handle_write_event()
return (WebviErr.OK, len(socket_map))
diff --git a/src/libwebvi/webvi/asyncurl.py b/src/libwebvi/webvi/asyncurl.py
index 585f090..6b17893 100644
--- a/src/libwebvi/webvi/asyncurl.py
+++ b/src/libwebvi/webvi/asyncurl.py
@@ -40,7 +40,8 @@ def poll(timeout=0.0, map=None, mdisp=None):
if mdisp is None:
mdisp = multi_dispatcher
if map:
- timeout = min(timeout, mdisp.timeout/1000.0)
+ if mdisp.timeout != -1:
+ timeout = min(timeout, mdisp.timeout/1000.0)
r = []; w = []; e = []
for fd, obj in map.items():
@@ -53,7 +54,8 @@ def poll(timeout=0.0, map=None, mdisp=None):
if is_r or is_w:
e.append(fd)
if [] == r == w == e:
- time.sleep(timeout)
+ if timeout > 0:
+ time.sleep(timeout)
else:
try:
r, w, e = select.select(r, w, e, timeout)
@@ -85,6 +87,11 @@ def poll(timeout=0.0, map=None, mdisp=None):
continue
asyncore._exception(obj)
+def poll_timeout(mdisp=None):
+ if mdisp is None:
+ mdisp = multi_dispatcher
+ mdisp.socket_action(SOCKET_TIMEOUT, 0)
+
def loop(timeout=30.0, use_poll=False, map=None, count=None, mdisp=None):
if map is None:
map = asyncore.socket_map
@@ -118,13 +125,15 @@ class curl_multi_dispatcher:
else:
self._map = socket_map
self.dispatchers = {}
- self.timeout = 1000
+ self.timeout = -1
+ # The lambda is to avoid "not callable" error from pylint
+ self.timeout_callback = lambda x, y: None
self._sockets_removed = False
self._curlm = pycurl.CurlMulti()
- self._curlm.setopt(pycurl.M_SOCKETFUNCTION, self.socket_callback)
- self._curlm.setopt(pycurl.M_TIMERFUNCTION, self.timeout_callback)
+ self._curlm.setopt(pycurl.M_SOCKETFUNCTION, self._socket_callback)
+ self._curlm.setopt(pycurl.M_TIMERFUNCTION, self._update_timeout)
- def socket_callback(self, action, socket, user_data, socket_data):
+ def _socket_callback(self, action, socket, user_data, socket_data):
# print 'socket callback: %d, %s' % \
# (socket, {pycurl.POLL_NONE: "NONE",
# pycurl.POLL_IN: "IN",
@@ -155,8 +164,10 @@ class curl_multi_dispatcher:
obj.set_readable(True)
obj.set_writable(True)
- def timeout_callback(self, msec):
+ def _update_timeout(self, msec):
self.timeout = msec
+ if self.timeout_callback:
+ self.timeout_callback(self, msec)
def attach(self, curldisp):
"""Starts a transfer on curl handle by attaching it to this
@@ -168,9 +179,9 @@ class curl_multi_dispatcher:
# the curl object is already on this multi-stack
pass
- while self._curlm.socket_all()[0] == pycurl.E_CALL_MULTI_PERFORM:
- pass
-
+ # _curlm.timeout() seems to be needed, although curl
+ # documentation doesn't mention it.
+ self._update_timeout(self._curlm.timeout())
self.check_completed(True)
def detach(self, curldisp):
@@ -181,9 +192,6 @@ class curl_multi_dispatcher:
# libcurl does not send POLL_REMOVE when a handle is aborted
for socket, curlobj in self._map.items():
if curlobj == curldisp:
-
- print 'handle stopped but socket in map'
-
del self._map[socket]
break
diff --git a/src/libwebvi/webvi/constants.py b/src/libwebvi/webvi/constants.py
index 2d29700..16e1f92 100644
--- a/src/libwebvi/webvi/constants.py
+++ b/src/libwebvi/webvi/constants.py
@@ -41,11 +41,16 @@ class WebviInfo:
STREAM_TITLE = 3
class WebviSelectBitmask:
- TIMEOUT = 0
+ CHECK = 0
READ = 1
WRITE = 2
EXCEPTION = 4
+class WebviSelect:
+ TIMEOUT = -1
+
class WebviConfig:
TEMPLATE_PATH = 0
DEBUG = 1
+ TIMEOUT_CALLBACK = 2
+ TIMEOUT_DATA = 3
diff --git a/src/libwebvi/webvi/request.py b/src/libwebvi/webvi/request.py
index 87dcf87..cab15f7 100644
--- a/src/libwebvi/webvi/request.py
+++ b/src/libwebvi/webvi/request.py
@@ -24,12 +24,14 @@ import download
import sys
import utils
import json2xml
+import asyncurl
from constants import WebviRequestType
DEBUG = False
DEFAULT_TEMPLATE_PATH = '/usr/local/share/webvi/templates'
template_path = DEFAULT_TEMPLATE_PATH
+timeout_data = None
def debug(msg):
if DEBUG:
@@ -49,6 +51,10 @@ def set_template_path(path):
debug("set_template_path " + template_path)
+def set_timeout_callback(callback):
+ c_api_callback = lambda mdisp, timeout: callback(timeout, timeout_data)
+ asyncurl.multi_dispatcher.timeout_callback = c_api_callback
+
def parse_reference(reference):
"""Parses URLs of the following form: