Upstream version 10.39.233.0
[platform/framework/web/crosswalk.git] / src / ozone / wayland / display_poll_thread.cc
1 // Copyright 2013 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.
4
5 #include "ozone/wayland/display_poll_thread.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/epoll.h>
10 #include <sys/socket.h>
11 #include <sys/types.h>
12 #include <wayland-client.h>
13
14 #include "base/bind.h"
15 #include "ozone/wayland/display.h"
16
17 namespace ozonewayland {
18 const int MAX_EVENTS = 16;
19 // os-compatibility
20 extern "C" {
21 int osEpollCreateCloExec(void);
22
23 static int setCloExecOrClose(int fd) {
24   int flags;
25
26   if (fd == -1)
27     return -1;
28
29   flags = fcntl(fd, F_GETFD);
30   if (flags == -1)
31     goto err;
32
33   if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
34     goto err;
35
36   return fd;
37
38   err:
39     close(fd);
40     return -1;
41 }
42
43 int osEpollCreateCloExec(void) {
44   int fd;
45
46 #ifdef EPOLL_CLOEXEC
47   fd = epoll_create1(EPOLL_CLOEXEC);
48   if (fd >= 0)
49     return fd;
50   if (errno != EINVAL)
51     return -1;
52 #endif
53
54   fd = epoll_create(1);
55   return setCloExecOrClose(fd);
56 }
57 }  // os-compatibility
58
59 WaylandDisplayPollThread::WaylandDisplayPollThread(wl_display* display)
60     : base::Thread("WaylandDisplayPollThread"),
61       display_(display),
62       polling_(true, false),
63       stop_polling_(true, false) {
64   DCHECK(display_);
65 }
66
67 WaylandDisplayPollThread::~WaylandDisplayPollThread() {
68   StopProcessingEvents();
69 }
70
71 void WaylandDisplayPollThread::StartProcessingEvents() {
72   DCHECK(!polling_.IsSignaled());
73   base::Thread::Options options;
74   options.message_loop_type = base::MessageLoop::TYPE_IO;
75   StartWithOptions(options);
76   SetPriority(base::kThreadPriority_Background);
77   message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
78       &WaylandDisplayPollThread::DisplayRun, this));
79 }
80
81 void WaylandDisplayPollThread::StopProcessingEvents() {
82   if (polling_.IsSignaled())
83     stop_polling_.Signal();
84
85   Stop();
86 }
87
88 void WaylandDisplayPollThread::CleanUp() {
89   SetThreadWasQuitProperly(true);
90 }
91
92 void  WaylandDisplayPollThread::DisplayRun(WaylandDisplayPollThread* data) {
93   struct epoll_event ep[MAX_EVENTS];
94   int i, ret, count = 0;
95   uint32_t event = 0;
96   bool epoll_err = false;
97   unsigned display_fd = wl_display_get_fd(data->display_);
98   int epoll_fd = osEpollCreateCloExec();
99   if (epoll_fd < 0) {
100     LOG(ERROR) << "Epoll creation failed.";
101     return;
102   }
103
104   ep[0].events = EPOLLIN;
105   ep[0].data.ptr = 0;
106   if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, display_fd, &ep[0]) < 0) {
107     close(epoll_fd);
108     LOG(ERROR) << "epoll_ctl Add failed";
109     return;
110   }
111
112   // Set the signal state. This is used to query from other threads (i.e.
113   // StopProcessingEvents on Main thread), if this thread is still polling.
114   data->polling_.Signal();
115
116   // Adopted from:
117   // http://cgit.freedesktop.org/wayland/weston/tree/clients/window.c#n5531.
118   while (1) {
119     wl_display_dispatch_pending(data->display_);
120     ret = wl_display_flush(data->display_);
121     if (ret < 0 && errno == EAGAIN) {
122       ep[0].events = EPOLLIN | EPOLLERR | EPOLLHUP;
123       epoll_ctl(epoll_fd, EPOLL_CTL_MOD, display_fd, &ep[0]);
124     } else if (ret < 0) {
125       epoll_err = true;
126       break;
127     }
128     // StopProcessingEvents has been called or we have been asked to stop
129     // polling. Break from the loop.
130     if (data->stop_polling_.IsSignaled())
131       break;
132
133     count = epoll_wait(epoll_fd, ep, MAX_EVENTS, -1);
134     // Break if epoll wait returned value less than 0 and we aren't interrupted
135     // by a signal.
136     if (count < 0 && errno != EINTR) {
137       LOG(ERROR) << "epoll_wait returned an error." << errno;
138       epoll_err = true;
139       break;
140     }
141
142     for (i = 0; i < count; i++) {
143       event = ep[i].events;
144       // We can have cases where EPOLLIN and EPOLLHUP are both set for
145       // example. Don't break if both flags are set.
146       if ((event & EPOLLERR || event & EPOLLHUP) &&
147              !(event & EPOLLIN)) {
148         epoll_err = true;
149         break;
150       }
151
152       if (event & EPOLLIN) {
153         ret = wl_display_dispatch(data->display_);
154         if (ret == -1) {
155           LOG(ERROR) << "wl_display_dispatch failed with an error." << errno;
156           epoll_err = true;
157           break;
158         }
159       }
160     }
161
162     if (epoll_err)
163       break;
164   }
165
166   close(epoll_fd);
167   data->polling_.Reset();
168   data->stop_polling_.Reset();
169 }
170
171 }  // namespace ozonewayland