Upstream version 11.39.252.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   epoll_fd_ = osEpollCreateCloExec();
66   if (epoll_fd_ < 0)
67     LOG(ERROR) << "Epoll creation failed.";
68 }
69
70 WaylandDisplayPollThread::~WaylandDisplayPollThread() {
71   StopProcessingEvents();
72 }
73
74 void WaylandDisplayPollThread::StartProcessingEvents() {
75   DCHECK(!polling_.IsSignaled() && epoll_fd_);
76   base::Thread::Options options;
77   options.message_loop_type = base::MessageLoop::TYPE_IO;
78   StartWithOptions(options);
79   SetPriority(base::kThreadPriority_Background);
80   message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
81       &WaylandDisplayPollThread::DisplayRun, this));
82 }
83
84 void WaylandDisplayPollThread::StopProcessingEvents() {
85   if (polling_.IsSignaled())
86     stop_polling_.Signal();
87
88   Stop();
89 }
90
91 void WaylandDisplayPollThread::CleanUp() {
92   SetThreadWasQuitProperly(true);
93   if (epoll_fd_)
94     close(epoll_fd_);
95 }
96
97 void  WaylandDisplayPollThread::DisplayRun(WaylandDisplayPollThread* data) {
98   struct epoll_event ep[MAX_EVENTS];
99   int i, ret, count = 0;
100   uint32_t event = 0;
101   bool epoll_err = false;
102   unsigned display_fd = wl_display_get_fd(data->display_);
103   int epoll_fd = data->epoll_fd_;
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     data->epoll_fd_ = 0;
109     LOG(ERROR) << "epoll_ctl Add failed";
110     return;
111   }
112
113   // Set the signal state. This is used to query from other threads (i.e.
114   // StopProcessingEvents on Main thread), if this thread is still polling.
115   data->polling_.Signal();
116
117   // Adopted from:
118   // http://cgit.freedesktop.org/wayland/weston/tree/clients/window.c#n5531.
119   while (1) {
120     wl_display_dispatch_pending(data->display_);
121     ret = wl_display_flush(data->display_);
122     if (ret < 0 && errno == EAGAIN) {
123       ep[0].events = EPOLLIN | EPOLLERR | EPOLLHUP;
124       epoll_ctl(epoll_fd, EPOLL_CTL_MOD, display_fd, &ep[0]);
125     } else if (ret < 0) {
126       epoll_err = true;
127       break;
128     }
129     // StopProcessingEvents has been called or we have been asked to stop
130     // polling. Break from the loop.
131     if (data->stop_polling_.IsSignaled())
132       break;
133
134     count = epoll_wait(epoll_fd, ep, MAX_EVENTS, -1);
135     // Break if epoll wait returned value less than 0 and we aren't interrupted
136     // by a signal.
137     if (count < 0 && errno != EINTR) {
138       LOG(ERROR) << "epoll_wait returned an error." << errno;
139       epoll_err = true;
140       break;
141     }
142
143     for (i = 0; i < count; i++) {
144       event = ep[i].events;
145       // We can have cases where EPOLLIN and EPOLLHUP are both set for
146       // example. Don't break if both flags are set.
147       if ((event & EPOLLERR || event & EPOLLHUP) &&
148              !(event & EPOLLIN)) {
149         epoll_err = true;
150         break;
151       }
152
153       if (event & EPOLLIN) {
154         ret = wl_display_dispatch(data->display_);
155         if (ret == -1) {
156           LOG(ERROR) << "wl_display_dispatch failed with an error." << errno;
157           epoll_err = true;
158           break;
159         }
160       }
161     }
162
163     if (epoll_err)
164       break;
165   }
166
167   data->polling_.Reset();
168   data->stop_polling_.Reset();
169 }
170
171 }  // namespace ozonewayland