4 /* Copyright 2002 The gtkmm Development Team
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <glibmm/dispatcher.h>
22 #include <glibmm/exceptionhandler.h>
23 #include <glibmm/fileutils.h>
24 #include <glibmm/main.h>
25 #include <glibmm/thread.h>
34 #if defined(_tru64) //TODO: Use the real define
35 //EINTR is not defined on Tru64
36 //I have tried including these
37 //#include <sys/types.h>
38 //#include <sys/statvfs.h>
50 #endif /* G_OS_WIN32 */
56 struct DispatchNotifyData
59 Glib::Dispatcher* dispatcher;
60 Glib::DispatchNotifier* notifier;
63 : tag (0), dispatcher (0), notifier (0) {}
65 DispatchNotifyData(unsigned long tag_, Glib::Dispatcher* dispatcher_, Glib::DispatchNotifier* notifier_)
66 : tag (tag_), dispatcher (dispatcher_), notifier (notifier_) {}
69 static void warn_failed_pipe_io(const char* what, int err_no)
72 const char *const message = g_win32_error_message(err_no);
74 const char *const message = g_strerror(err_no);
76 g_critical("Error in inter-thread communication: %s() failed: %s", what, message);
81 * Try to set the close-on-exec flag of the file descriptor,
82 * so that it won't be leaked if a new process is spawned.
84 static void fd_set_close_on_exec(int fd)
86 const int flags = fcntl(fd, F_GETFD, 0);
87 g_return_if_fail(flags >= 0);
89 fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
91 #endif /* !G_OS_WIN32 */
97 static void fd_close_and_invalidate(HANDLE& fd)
102 warn_failed_pipe_io("CloseHandle", GetLastError());
107 #else /* !G_OS_WIN32 */
108 static void fd_close_and_invalidate(int& fd)
116 while(result < 0 && errno == EINTR);
119 warn_failed_pipe_io("close", errno);
124 #endif /* !G_OS_WIN32 */
126 } // anonymous namespace
132 class DispatchNotifier : public sigc::trackable
137 static DispatchNotifier* reference_instance(const Glib::RefPtr<MainContext>& context);
138 static void unreference_instance(DispatchNotifier* notifier);
140 void send_notification(Dispatcher* dispatcher);
143 // Only used by reference_instance(). Should be private, but that triggers
144 // a silly gcc warning even though DispatchNotifier has static methods.
145 explicit DispatchNotifier(const Glib::RefPtr<MainContext>& context);
148 static Glib::StaticPrivate<DispatchNotifier> thread_specific_instance_;
150 Glib::RefPtr<MainContext> context_;
155 std::list<DispatchNotifyData> notify_queue_;
159 #endif /* !G_OS_WIN32 */
160 sigc::connection conn_io_handler_;
163 bool pipe_io_handler(Glib::IOCondition condition);
166 DispatchNotifier(const DispatchNotifier&);
167 DispatchNotifier& operator=(const DispatchNotifier&);
171 /**** Glib::DispatchNotifier ***********************************************/
173 Glib::StaticPrivate<DispatchNotifier>
174 DispatchNotifier::thread_specific_instance_ = GLIBMM_STATIC_PRIVATE_INIT;
176 DispatchNotifier::DispatchNotifier(const Glib::RefPtr<MainContext>& context)
191 #ifdef GLIBMM_EXCEPTIONS_ENABLED
194 #endif //GLIBMM_EXCEPTIONS_ENABLED
196 conn_io_handler_ = context_->signal_io().connect(
197 sigc::mem_fun(*this, &DispatchNotifier::pipe_io_handler),
198 GPOINTER_TO_INT(fd_receiver_), Glib::IO_IN);
199 #else /* !G_OS_WIN32 */
200 conn_io_handler_ = context_->signal_io().connect(
201 sigc::mem_fun(*this, &DispatchNotifier::pipe_io_handler),
202 fd_receiver_, Glib::IO_IN);
203 #endif /* !G_OS_WIN32 */
204 #ifdef GLIBMM_EXCEPTIONS_ENABLED
209 fd_close_and_invalidate(fd_sender_);
210 #endif /* !G_OS_WIN32 */
211 fd_close_and_invalidate(fd_receiver_);
215 #endif //GLIBMM_EXCEPTIONS_ENABLED
218 DispatchNotifier::~DispatchNotifier()
220 // Disconnect manually because we don't inherit from sigc::trackable
221 conn_io_handler_.disconnect();
224 fd_close_and_invalidate(fd_sender_);
225 #endif /* !G_OS_WIN32 */
226 fd_close_and_invalidate(fd_receiver_);
229 void DispatchNotifier::create_pipe()
232 // On Win32 we are using synchronization object instead of pipe
233 // thus storing its handle as fd_receiver_.
234 fd_receiver_ = CreateEvent(0, FALSE, FALSE, 0);
238 #ifdef GLIBMM_EXCEPTIONS_ENABLED
239 GError* const error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_FAILED,
240 "Failed to create event for inter-thread communication: %s",
241 g_win32_error_message(GetLastError()));
242 throw Glib::FileError(error);
244 return; //TODO: Provide an alternative to the exception.
245 #endif //GLIBMM_EXCEPTIONS_ENABLED
248 #else /* !G_OS_WIN32 */
249 int filedes[2] = { -1, -1 };
251 if(pipe(filedes) < 0)
253 #ifdef GLIBMM_EXCEPTIONS_ENABLED
254 GError* const error = g_error_new(G_FILE_ERROR, g_file_error_from_errno(errno),
255 "Failed to create pipe for inter-thread communication: %s",
257 throw Glib::FileError(error);
259 return; //TODO: Provide an alternative to the exception.
260 #endif //GLIBMM_EXCEPTIONS_ENABLED
263 fd_set_close_on_exec(filedes[0]);
264 fd_set_close_on_exec(filedes[1]);
266 fd_receiver_ = filedes[0];
267 fd_sender_ = filedes[1];
268 #endif /* !G_OS_WIN32 */
272 DispatchNotifier* DispatchNotifier::reference_instance(const Glib::RefPtr<MainContext>& context)
274 DispatchNotifier* instance = thread_specific_instance_.get();
278 instance = new DispatchNotifier(context);
279 thread_specific_instance_.set(instance);
283 // Prevent massive mess-up.
284 g_return_val_if_fail(instance->context_ == context, 0);
287 ++instance->ref_count_; // initially 0
293 void DispatchNotifier::unreference_instance(DispatchNotifier* notifier)
295 DispatchNotifier *const instance = thread_specific_instance_.get();
297 // Yes, the notifier argument is only used to check for sanity.
298 g_return_if_fail(instance == notifier);
300 if(--instance->ref_count_ <= 0)
302 g_return_if_fail(instance->ref_count_ == 0); // could be < 0 if messed up
304 // This will cause deletion of the notifier object.
305 thread_specific_instance_.set(0);
309 void DispatchNotifier::send_notification(Dispatcher* dispatcher)
313 Glib::Mutex::Lock lock (mutex_);
314 notify_queue_.push_back(DispatchNotifyData(0xdeadbeef, dispatcher, this));
317 // Send notification event to GUI-thread.
318 if(!SetEvent(fd_receiver_))
320 warn_failed_pipe_io("SetEvent", GetLastError());
323 #else /* !G_OS_WIN32 */
324 DispatchNotifyData data (0xdeadbeef, dispatcher, this);
328 n_written = write(fd_sender_, &data, sizeof(data));
329 while(n_written < 0 && errno == EINTR);
333 warn_failed_pipe_io("write", errno);
337 // All data must be written in a single call to write(), otherwise we can't
338 // guarantee reentrancy since another thread might be scheduled between two
339 // write() calls. The manpage is a bit unclear about this -- but I hope
340 // it's safe to assume immediate success for the tiny amount of data we're
342 g_return_if_fail(n_written == sizeof(data));
343 #endif /* !G_OS_WIN32 */
346 bool DispatchNotifier::pipe_io_handler(Glib::IOCondition)
349 DispatchNotifyData data;
354 Glib::Mutex::Lock lock (mutex_);
356 if(notify_queue_.empty())
359 data = notify_queue_.front();
360 notify_queue_.pop_front();
363 g_return_val_if_fail(data.tag == 0xdeadbeef, true);
364 g_return_val_if_fail(data.notifier == this, true);
366 // Actually, we wouldn't need the try/catch block because the Glib::Source
367 // C callback already does it for us. However, we do it anyway because the
368 // default return value is 'false', which is not what we want.
369 #ifdef GLIBMM_EXCEPTIONS_ENABLED
372 #endif //GLIBMM_EXCEPTIONS_ENABLED
373 data.dispatcher->signal_();
374 #ifdef GLIBMM_EXCEPTIONS_ENABLED
378 Glib::exception_handlers_invoke();
380 #endif //GLIBMM_EXCEPTIONS_ENABLED
382 #else /* !G_OS_WIN32 */
383 DispatchNotifyData data;
388 void * const buffer = reinterpret_cast<guint8*>(&data) + n_read;
389 const gssize result = read(fd_receiver_, buffer, sizeof(data) - n_read);
396 warn_failed_pipe_io("read", errno);
402 while(n_read < sizeof(data));
404 g_return_val_if_fail(data.tag == 0xdeadbeef, true);
405 g_return_val_if_fail(data.notifier == this, true);
407 // Actually, we wouldn't need the try/catch block because the Glib::Source
408 // C callback already does it for us. However, we do it anyway because the
409 // default return value is 'false', which is not what we want.
410 #ifdef GLIBMM_EXCEPTIONS_ENABLED
413 #endif //GLIBMM_EXCEPTIONS_ENABLED
414 data.dispatcher->signal_(); // emit
415 #ifdef GLIBMM_EXCEPTIONS_ENABLED
419 Glib::exception_handlers_invoke();
421 #endif //GLIBMM_EXCEPTIONS_ENABLED
422 #endif /* !G_OS_WIN32 */
428 /**** Glib::Dispatcher *****************************************************/
430 Dispatcher::Dispatcher()
433 notifier_ (DispatchNotifier::reference_instance(MainContext::get_default()))
436 Dispatcher::Dispatcher(const Glib::RefPtr<MainContext>& context)
439 notifier_ (DispatchNotifier::reference_instance(context))
442 Dispatcher::~Dispatcher()
444 DispatchNotifier::unreference_instance(notifier_);
447 void Dispatcher::emit()
449 notifier_->send_notification(this);
452 void Dispatcher::operator()()
457 sigc::connection Dispatcher::connect(const sigc::slot<void>& slot)
459 return signal_.connect(slot);