1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "xwalk/tizen/browser/media/murphy_mainloop.h"
8 #include <murphy/common/macros.h>
9 #include <murphy/common/log.h>
10 #include <murphy/common/mm.h>
11 #include <sys/socket.h>
13 #include "base/message_loop/message_loop.h"
14 #include "base/message_loop/message_pump_libevent.h"
15 #include "content/public/browser/browser_thread.h"
18 // The Murphy resource libraries don't have interfaces that would allow one
19 // to take externally care of the IPC necessary for communicating with the
20 // server (the policy decision making entity). Instead these libraries always
21 // operate within the context of a Murphy mainloop abstraction which by design
22 // can be set up to be pumped by an external event loop. The MurphyMainloop
23 // class below creates a Murphy mainloop and takes care of the details of
24 // adapting it to be pumped by the local MessageLoop infrastructure.
26 // The Murphy mainloop needs abstractions for I/O watches, timers, and deferred
27 // callbacks from the hosting environment. If necessary it can get along with
28 // just I/O watches and timers and emulate deferred callbacks by low-delay
29 // timers. Murphy needs timers to be both cancellable and modifiable and it
30 // deferred callbacks that can be disabled and re-enabled.
32 // The available infrastructure here offers the basic building blocks for all of
33 // these but none of them are a perfect fit. Here is the summary of the tweaks
34 // we need (in the hope) to get the Murphy mainloop running:
36 // 1) You can only do async I/O in the content::BrowserThread::IO thread.
38 // We're running in content::BrowserThread::UI so we can't manipulate
39 // FileDescriptorWatchers there. To overcome this we need to relay all
40 // operations on FileDescriptorWatchers to content::BrowserThread::IO
41 // by PostTask. This will result in getting all the I/O events delivered
42 // to content::BrowserThread::IO. However, since we don't want to set up
43 // a (potentially) complex locking scheme to protect access to the mainloop
44 // we also relay FileDescriptorWatcher notifications back to the UI thread
45 // for processing. Finally to make sure there are no pending events in
46 // flight by the time we destroy an I/O watch we essentially drive the
47 // destructor through a full UI -> IO -> UI thread relay.
49 // 2) FileDescriptorWatchers don't have HUP events.
51 // As a POSIX-specific hack we MSG_PEEK and generate a synthetic HUP event
54 // 3) You can't cancel a PostTask'd or PostDelayedTask'd task.
56 // This prevents us from having a straightforward mapping of deferred
57 // callbacks and timers to these. To overcome this we use a 'timeout finger-
58 // print' which is used to ignore timeout callbacks that should have been
59 // cancelled if only there was a mechanism for it. The fingerprint in simply
60 // a monotonically increasing integer which is stored in the timer and also
61 // associated with a pending timeout. Whenever the timer is reconfigured
62 // (delay updated, or timer disabled) the fingerprint is updated. Timeout
63 // callbacks with mismatching fingerprints are ignored.
65 // Originally we simply scheduled a task from the I/O thread to the UI thread
66 // for reading the pending epoll events there. But since poll/select is level-
67 // triggered by default this caused a large amount of messages before the
68 // scheduler got around to run the UI thread an read the events. Therefore now
69 // the Murphy mainloop has been opened up for doing the actual event retrieval
70 // externally to better support pumping it from xwalk. This now starts to be
71 // hairier than I'm comfortable with... I think it'd be a better idea to run
72 // the mainloop fully in the I/O thread, have the resource sets live in the I/O
73 // thread also and have proxy object for them attached to the media backend
74 // objects which would send requests and receive notifications to/from the
75 // resource sets. We could avoid most of the threading related compilations
76 // for pumping the mainloop...
78 // Additionally to the basic mainloop adaptation we also set up the Murphy
79 // debugging and logging infrastructure to use the native logging infra as
80 // a backend. You can control the Murphy logging and debugging by two
81 // environment variables:
84 // A comma separated list of log levels, defaults to 'info,warning,error'
86 // XWALK_MURPHY_DEBUG:
87 // A comma-separated list of Murphy debug sites, for instance
88 // '@mainloop.c,@resource.c,mrp_resource_set_create'. Setting this to
89 // '*' will turn all debug sites on.
93 // Environment variable names to used to control debugging and logging
94 #define ENVVAR_DBG "XWALK_MURPHY_DEBUG"
95 #define ENVVAR_LOG "XWALK_MURPHY_LOG"
99 static void xwalklogger(void* data, mrp_log_level_t level, const char* file,
100 int line, const char* func, const char* format,
103 char msg[1024], locbuf[1024];
112 if (level != MRP_LOG_DEBUG) {
115 snprintf(locbuf, sizeof(locbuf), "[%s] ", func ? func : "<unknown>");
119 if (vsnprintf(msg, sizeof(msg), format, cp) < (ssize_t)sizeof(msg)) {
121 case MRP_LOG_INFO: LOG(INFO) << loc << msg; break;
122 case MRP_LOG_WARNING: LOG(WARNING) << loc << msg; break;
123 case MRP_LOG_ERROR: LOG(ERROR) << loc << msg; break;
124 case MRP_LOG_DEBUG: DLOG(INFO) << loc << msg; break;
132 // Helper function to check if we have a pending HUP on an fd.
133 static int pending_hup(int fd) {
135 int len, saved_errno;
138 len = recv(fd, &buf, 1, MSG_PEEK);
148 // An I/O watch abstraction for Murphy mainloop integration.
150 // As stated above, FileDescriptorWatcher's can only be manipulated
151 // in BrowserThread::IO. As we execute in BrowserThread::UI we need
152 // to relay watch manipulation operations to the I/O thread and
153 // relay events back to the UI thread.
154 class IoWatch : public base::MessagePumpLibevent::Watcher {
156 // Constructor. Saves context and relays the watch setup to the I/O thread.
157 IoWatch(MurphyMainloop* mainloop, int fd, mrp_io_event_t events,
158 void (*cb)(void* glue_data, void* id, int fd, mrp_io_event_t events,
159 void* user_data), void* user_data)
160 : mainloop_(mainloop),
164 user_data_(user_data),
166 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
167 base::Bind(&IoWatch::StartWatch,
168 base::Unretained(this)));
171 // Request destruction. Mark dead, relay watch cleanup to the I/O thread.
172 // The destruction sequence is finished once StopWatch relays WatchStopped
173 // back to the UI thread.
178 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
179 base::Bind(&IoWatch::StopWatch,
180 base::Unretained(this)));
184 // Perform watch setup. Run in the I/O thread.
186 base::MessageLoopForIO::Mode mode;
188 if (events_ & (MRP_IO_EVENT_IN | MRP_IO_EVENT_OUT))
189 mode = base::MessageLoopForIO::WATCH_READ_WRITE;
190 else if (events_ & MRP_IO_EVENT_IN)
191 mode = base::MessageLoopForIO::WATCH_READ;
192 else if (events_ & MRP_IO_EVENT_OUT)
193 mode = base::MessageLoopForIO::WATCH_WRITE;
195 mode = base::MessageLoopForIO::WATCH_READ; // Hmm... not quite right.
197 const bool success = base::MessageLoopForIO::current()->WatchFileDescriptor(
198 fd_, true, mode, &w_, this);
199 CHECK(success) << "Failed to add I/O watch for fd " << fd_;
202 // Perform watch cleanup. Run in I/O thread.
208 w_.StopWatchingFileDescriptor();
210 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
211 base::Bind(&IoWatch::WatchStopped,
212 base::Unretained(this)));
215 // Finish the destruction sequence by self-deleting. Run in UI thread.
216 void WatchStopped() {
220 // Watch readability event handler. Run in I/O thread. Relays dispatching
222 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
226 mainloop_->Poll(this);
228 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
229 base::Bind(&IoWatch::DispatchReadable,
230 base::Unretained(this)));
233 // Watch writability event handler. Run in I/O thread. Relays dispatching
235 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {
239 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
240 base::Bind(&IoWatch::DispatchWritable,
241 base::Unretained(this)));
244 // Dispatch readability events the the mainloop.
245 void DispatchReadable() {
246 mrp_io_event_t events;
250 if ((events_ & MRP_IO_EVENT_HUP) && pending_hup(fd_))
251 events = MRP_IO_EVENT_HUP;
253 events = MRP_IO_EVENT_IN;
255 mrp_debug("dispatching crosswalk event 0x%x for fd %d", events, fd_);
256 cb_(reinterpret_cast<void*>(mainloop_), reinterpret_cast<void*>(this),
257 fd_, events, user_data_);
260 // Dispatch writability events the the mainloop.
261 void DispatchWritable() {
262 mrp_io_event_t events;
266 events = MRP_IO_EVENT_OUT;
267 mrp_debug("dispatching crosswalk event 0x%x for fd %d", events, fd_);
268 cb_(reinterpret_cast<void*>(mainloop_), reinterpret_cast<void*>(this),
269 fd_, events, user_data_);
272 // Associated mainloop data: fd, event mask, callback, and user data.
273 MurphyMainloop* mainloop_;
275 mrp_io_event_t events_;
276 void (*cb_)(void* glue_data, void* id, int fd, mrp_io_event_t events,
280 // FileDescriptorWatcher we use for I/O monitoring
281 base::MessagePumpLibevent::FileDescriptorWatcher w_;
283 // flag used to mark initiated destruction sequence
286 // A self-destructing object, so we have a private destructor
288 mrp_debug("Destructing I/O watch");
292 // A Timer abstraction for Murphy mainloop integration.
294 // Since posted tasks cannot be cancelled, we use the stamped Timeout
295 // object to filter out callbacks for timers that have been cancelled
296 // (or reconfigured).
299 class Timeout : public base::RefCounted<Timeout> {
301 explicit Timeout(Timer* t)
305 // Release upon timer destruction. We need to keep this object around
310 // Clear Timeout for destruction. After this point all events are ignored.
311 // We're just waiting for the last reference to be Release()'d which is
312 // immediate if we have no pending timeouts. Otherwise it ought to happen
313 // once the last timeout has expired.
319 // Arm or rearm the timeout with the given delay.
320 void Arm(int delay) {
321 base::TimeDelta delta(base::TimeDelta::FromMilliseconds(delay));
323 content::BrowserThread::PostDelayedTask(content::BrowserThread::UI,
325 base::Bind(&Timeout::Expired,
326 this, stamp_), delta);
337 // Invalidate any possible pending Timeouts.
338 inline void InvalidatePending() {
343 // Timeout handler callback. Filter cancelled timeouts, deliver valid
344 // timeouts to the parent Timer.
345 void Expired(unsigned int stamp);
348 mrp_debug("destructing Timeout %p", this);
351 // We're refcounted and have a private destructor...
352 friend class base::RefCounted<Timeout>;
354 // Timer we're serving.
357 // Fingerprint stamp.
360 // Flag to mark if we have pending timeouts.
366 Timer(MurphyMainloop* mainloop, int delay,
367 void (*cb)(void* glue_data, void* id, void* user_data),
369 : mainloop_(mainloop),
371 user_data_(user_data),
375 timeout_ = new Timeout(this);
379 // Upon timer destruction, initiate timeout destruction sequence and release
380 // its initial reference.
385 // Change timer delay.
386 void SetDelay(int delay) {
406 // Re-arm the timer with the current delay if it is enabled.
408 if (!enabled_ || delay_ == -1)
411 mrp_debug("rearming timer %p (delay %d)", this, delay_);
413 timeout_->Arm(delay_);
416 // Timeout handler callback.
417 void DispatchTimer() {
419 mrp_debug("dispatching crosswalk timeout event %p", this);
420 cb_(reinterpret_cast<void*>(mainloop_), reinterpret_cast<void*>(this),
429 // Let Timeout invoke DispatchTimer.
430 friend class Timeout;
432 // Associated mainloop data: timer callback and user data.
433 MurphyMainloop* mainloop_;
434 void (*cb_)(void* glue_data, void* id, void* user_data);
437 // Our timeout in milliseconds.
440 // Whether we're enabled.
443 // Our associated timeout.
447 void Timeout::Expired(unsigned int stamp) {
448 if (timer_ == NULL || stamp != stamp_)
453 timer_->DispatchTimer();
456 MurphyMainloop::MurphyMainloop(const char* log, const char* debug)
459 mrp_list_init(&poll_q_);
460 setupLogger(log, debug);
462 CHECK(setupMainloop());
465 MurphyMainloop::~MurphyMainloop() {
466 mrp_debug("destroying MurphyMainloop");
467 mrp_mainloop_destroy(mainloop_);
471 // Crosswalk mainloop abstraction operations
472 void* MurphyMainloop::AddIo(void* glue_data, int fd, mrp_io_event_t events,
473 void (*cb)(void* glue_data, void* id, int fd, mrp_io_event_t events,
474 void* user_data), void* user_data) {
476 MurphyMainloop* self = static_cast<MurphyMainloop*>(glue_data);
478 CHECK(self->poll_id_ == NULL);
480 self->poll_id_ = new IoWatch(self, fd, events, cb, user_data);
482 mrp_debug("added I/O Watch %p for fd %d (glue_data: %p)", self->poll_id_, fd,
485 return self->poll_id_;
489 void MurphyMainloop::DelIo(void* glue_data, void* id) {
490 IoWatch* w = static_cast<IoWatch*>(id);
492 mrp_debug("deleting I/O Watch %p", id);
499 mrp_list_hook_t hook;
506 void MurphyMainloop::Poll(void* id) {
509 mrp_debug("polling pending epoll events for the mainloop (id: %p)", id);
512 CHECK(poll_id_ == id);
515 if (poll_id_ != id || !poll_events_)
519 item = reinterpret_cast<pollq_data_t *>(mrp_allocz(sizeof(*item)));
524 mrp_list_init(&item->hook);
525 item->size = poll_events_(id, mainloop_, &item->buf);
527 poll_lock_.Acquire();
528 mrp_list_append(&poll_q_, &item->hook);
529 poll_lock_.Release();
534 size_t MurphyMainloop::PollIo(void* glue_data,
535 void* id, void* buf, size_t size) {
536 MurphyMainloop* self = static_cast<MurphyMainloop*>(glue_data);
539 mrp_debug("dispatching epoll events to the mainloop (id: %p)", id);
542 CHECK(self->poll_id_ == id);
544 if (self->poll_id_ != id)
548 self->poll_lock_.Acquire();
549 if (!mrp_list_empty(&self->poll_q_)) {
550 item = mrp_list_entry(self->poll_q_.next, pollq_data_t, hook);
551 mrp_list_delete(&item->hook);
556 self->poll_lock_.Release();
559 CHECK(size >= item->size);
562 memcpy(buf, item->buf, size);
573 void* MurphyMainloop::AddTimer(void* glue_data, unsigned int msecs,
574 void (*cb)(void* glue_data, void* id, void* user_data),
576 MurphyMainloop* self = static_cast<MurphyMainloop*>(glue_data);
577 mrp_debug("adding Timer with %u msecs, %p user data", msecs, user_data);
579 return new Timer(self, static_cast<int>(msecs), cb, user_data);
583 void MurphyMainloop::DelTimer(void* glue_data, void* id) {
584 Timer* t = reinterpret_cast<Timer*>(id);
585 MRP_UNUSED(glue_data);
587 mrp_debug("deleting Timer %p", id);
592 void MurphyMainloop::ModTimer(void* glue_data, void* id, unsigned int msecs) {
593 Timer* t = reinterpret_cast<Timer*>(id);
594 MRP_UNUSED(glue_data);
596 mrp_debug("modifying Timer %p to %u msecs", t, msecs);
601 void* MurphyMainloop::AddDefer(void* glue_data,
602 void (*cb)(void* glue_data, void* id, void* user_data),
604 mrp_debug("adding deferred callback (cb:%p, user data:%p)", cb, user_data);
605 return AddTimer(glue_data, 0, cb, user_data);
609 void MurphyMainloop::DelDefer(void* glue_data, void* id) {
610 MRP_UNUSED(glue_data);
612 mrp_debug("deleting deferred callback %p", id);
614 DelTimer(glue_data, id);
618 void MurphyMainloop::ModDefer(void* glue_data, void* id, int enabled) {
619 MRP_UNUSED(glue_data);
620 Timer* t = reinterpret_cast<Timer*>(id);
622 mrp_debug("%sabling deferred callback %p", enabled ? "en" : "dis", id);
631 void MurphyMainloop::Unregister(void* data) {
634 mrp_debug("unregistering mainloop with data %p", data);
637 bool MurphyMainloop::setupMainloop() {
638 mainloop_ = mrp_mainloop_create();
639 mrp_set_io_event_mode(mainloop_, MRP_IO_TRIGGER_EDGE);
641 static mrp_superloop_ops_t ops = {
642 &MurphyMainloop::AddIo,
643 &MurphyMainloop::DelIo,
644 &MurphyMainloop::AddTimer,
645 &MurphyMainloop::DelTimer,
646 &MurphyMainloop::ModTimer,
647 &MurphyMainloop::AddDefer,
648 &MurphyMainloop::DelDefer,
649 &MurphyMainloop::ModDefer,
650 &MurphyMainloop::Unregister,
652 &MurphyMainloop::PollIo,
655 if (mrp_set_superloop(mainloop_, &ops, this)) {
656 poll_events_ = ops.poll_events;
659 mrp_log_error("Failed to set up superloop.");
664 // Murphy crosswalk logging backend
665 void MurphyMainloop::setupLogger(const char* logcfg, const char* dbgcfg) {
666 static bool registered = false;
667 const char *dbg, *log, *p, *n;
674 if (mrp_log_register_target("xwalk", xwalklogger, NULL))
675 mrp_log_set_target("xwalk");
677 // configure logging, environment variable overrides argument
678 if ((log = getenv(ENVVAR_LOG)) == NULL)
681 mrp_log_enable(mrp_log_parse_levels(log));
683 // configure debugging, environment variable overrides argument
684 if ((dbg = getenv(ENVVAR_DBG)) == NULL)
685 dbg = dbgcfg ? dbgcfg : "off";
687 if (strcmp(dbg, "off")) {
688 mrp_log_info("Enabling Murphy debugging (%s).", dbg);
689 mrp_debug_enable(true);
694 l = n ? n - p : strlen(p);
696 if (l < sizeof(site) - 1) {
699 mrp_log_info("Enabling Murphy debug site '%s'.", site);
700 mrp_debug_set_config(site);
702 p = n ? n + 1 : NULL;