[gtkmm] Dispatcher
- From: "Andrew E. Makeev" <andrew solvo ru>
- To: gtkmm-main <gtkmm-list gnome org>
- Subject: [gtkmm] Dispatcher
- Date: Wed, 22 Jan 2003 12:06:09 +0300
First of all, really sorry for delay. Unfortunatelly, we couldnt catch
gtkmm release.
Here we found the way the Dispatcher class could be used on Win32
systems as well as on UNIX based machines. I guess, there is some people
who gonna be happy to have this functionality in their projects.
There is only attached file (no patch), just to show how we could do
this to some person (Cedric, I guess), who was taking care about Win32
compatibility all the time. Use this file, fix Makefile for glibmm (no
more changes needed), then use dispatcher example files for testing.
Hope, it gonna work as well as for us.
Regards,
-andrew
// -*- c++ -*-
/* $Id: dispatcher.cc,v 1.8 2002/10/11 18:05:43 murrayc Exp $ */
/* Copyright 2002 The gtkmm Development Team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include <glibmm/dispatcher.h>
#include <glibmm/exceptionhandler.h>
#include <glibmm/fileutils.h>
#include <glibmm/main.h>
#include <glibmm/thread.h>
#include <sigc++/class_slot.h>
#include <cerrno>
#include <glib.h>
#include <fcntl.h>
#ifndef G_OS_WIN32
#include <unistd.h>
#else
#include <list>
/* This is what glib/gspawn-win32.c includes, I've no idea whether it works :) */
#include <windows.h>
#include <io.h>
#include <direct.h>
#endif /* G_OS_WIN32 */
namespace
{
#ifdef GTKMM_WIN32
template<class TQ> class Locked_Queue
{
private:
Glib::Mutex mutex_;
std::list<TQ*> lqueue_;
static TQ* delete_ptr (TQ* p) { delete p; return 0; }
public:
Locked_Queue<TQ>() : mutex_(), lqueue_() {}
~Locked_Queue<TQ>()
{
transform ( lqueue_.begin(),
lqueue_.end(),
lqueue_.begin(),
delete_ptr );
}
TQ* get (void)
{
TQ* ret = 0;
Glib::Mutex::Lock lock ( mutex_ );
if ( lqueue_.size() > 0)
{
ret = lqueue_.front();
lqueue_.pop_front();
}
return ret;
}
void put ( TQ* e )
{
Glib::Mutex::Lock lock ( mutex_ );
lqueue_.push_back ( e );
}
};
#define G_ERR(err_no) g_win32_error_message (err_no)
#define FD_TYPE HANDLE
#define INVALID_FD (0)
#else
#define G_ERR(err_no) g_strerror (err_no)
#define FD_TYPE int
#define INVALID_FD (-1)
#endif
struct DispatchNotifyData
{
unsigned long tag;
Glib::Dispatcher* dispatcher;
Glib::DispatchNotifier* notifier;
};
void warn_failed_pipe_io(const char* what, int err_no)
{
g_warning("Error in inter-thread communication: %s() failed: %s", what, G_ERR( err_no ) );
}
/* Try to set the close-on-exec flag of the file descriptor,
* so that it won't be leaked if a new process is spawned.
*/
#ifndef GTKMM_WIN32
void fd_set_close_on_exec(int fd)
{
const int flags = fcntl(fd, F_GETFD, 0);
g_return_if_fail(flags >= 0);
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
#endif
/* One word: paranoia.
*/
void fd_close_and_invalidate( FD_TYPE& fd )
{
if( fd != INVALID_FD )
{
#ifdef GTKMM_WIN32
if ( !CloseHandle ( fd ) )
warn_failed_pipe_io( "close", GetLastError() );
#else
int result;
do { result = close( fd ); }
while(result < 0 && errno == EINTR);
if(result < 0)
warn_failed_pipe_io("close", errno);
#endif
fd = INVALID_FD;
}
}
} // anonymous namespace
namespace Glib
{
class DispatchNotifier
{
public:
~DispatchNotifier();
static DispatchNotifier* reference_instance(const Glib::RefPtr<MainContext>& context);
static void unreference_instance(DispatchNotifier* notifier);
void send_notification(Dispatcher* dispatcher);
bool pipe_io_handler(Glib::IOCondition condition);
protected:
// Only used by reference_instance(). Should be private, but that triggers
// a silly gcc warning even though DispatchNotifier has static methods.
explicit DispatchNotifier(const Glib::RefPtr<MainContext>& context);
private:
static Glib::StaticPrivate<DispatchNotifier> thread_specific_instance_;
Glib::RefPtr<MainContext> context_;
int ref_count_;
FD_TYPE fd_receiver_;
#ifdef GTKMM_WIN32
Locked_Queue<DispatchNotifyData> notify_queue_;
#else
FD_TYPE fd_sender_;
#endif
SigC::Connection conn_io_handler_;
void create_pipe();
// noncopyable
DispatchNotifier(const DispatchNotifier&);
DispatchNotifier& operator=(const DispatchNotifier&);
};
/**** Glib::DispatchNotifier ***********************************************/
Glib::StaticPrivate<DispatchNotifier>
DispatchNotifier::thread_specific_instance_ = GLIBMM_STATIC_PRIVATE_INIT;
DispatchNotifier::DispatchNotifier(const Glib::RefPtr<MainContext>& context)
:
context_ (context),
ref_count_ (0),
fd_receiver_ (INVALID_FD),
#ifdef GTKMM_WIN32
notify_queue_ ()
#else
fd_sender_ (INVALID_FD)
#endif
{
create_pipe();
conn_io_handler_ = context_->signal_io().connect(
SigC::slot_class(*this, &DispatchNotifier::pipe_io_handler),
(int)fd_receiver_, Glib::IO_IN);
}
DispatchNotifier::~DispatchNotifier()
{
// Disconnect manually because we're using SigC::slot_class().
conn_io_handler_.disconnect();
#ifndef GTKMM_WIN32
fd_close_and_invalidate(fd_sender_);
#endif
fd_close_and_invalidate(fd_receiver_);
}
void DispatchNotifier::create_pipe()
{
#ifdef GTKMM_WIN32
if ( !(fd_receiver_ = CreateEvent (NULL, FALSE, FALSE, NULL)) )
{
gint err_no = GetLastError ();
GError *const error = g_error_new(
G_FILE_ERROR, g_file_error_from_errno( err_no ),
"Failed to create pipe for inter-thread communication: %s",
g_win32_error_message( err_no ) );
throw Glib::FileError( error );
}
#else
int filedes[2] = { -1, -1 };
if(pipe(filedes) < 0)
{
GError *const error = g_error_new(
G_FILE_ERROR, g_file_error_from_errno(errno),
"Failed to create pipe for inter-thread communication: %s", g_strerror(errno));
throw Glib::FileError(error);
}
fd_receiver_ = filedes[0];
fd_sender_ = filedes[1];
fd_set_close_on_exec(fd_receiver_);
fd_set_close_on_exec(fd_sender_);
#endif
}
// static
DispatchNotifier* DispatchNotifier::reference_instance(const Glib::RefPtr<MainContext>& context)
{
DispatchNotifier* instance = thread_specific_instance_.get();
if(!instance)
{
instance = new DispatchNotifier(context);
thread_specific_instance_.set(instance);
}
else
{
// Prevent massive mess-up.
g_return_val_if_fail(instance->context_ == context, 0);
}
++instance->ref_count_; // initially 0
return instance;
}
// static
void DispatchNotifier::unreference_instance(DispatchNotifier* notifier)
{
DispatchNotifier *const instance = thread_specific_instance_.get();
// Yes, the notifier argument is only used to check for sanity.
g_return_if_fail(instance == notifier);
if(--instance->ref_count_ <= 0)
{
g_return_if_fail(instance->ref_count_ == 0); // could be < 0 if messed up
// This will cause deletion of the notifier object.
thread_specific_instance_.set(0);
}
}
void DispatchNotifier::send_notification(Dispatcher* dispatcher)
{
#ifdef GTKMM_WIN32
DispatchNotifyData* data = new DispatchNotifyData();
data->tag = 0xdeadbeef;
data->dispatcher = dispatcher;
data->notifier = this;
notify_queue_.put ( data );
if ( !SetEvent ( fd_receiver_ ) )
{
warn_failed_pipe_io( "write", GetLastError() );
return;
}
#else
DispatchNotifyData data =
{
0xdeadbeef,
dispatcher,
this
};
gsize n_written = 0;
// Look at this code -- we're prepared for the worst. Hah!
do
{
void * const buffer = reinterpret_cast<guint8*>(&data) + n_written;
const gssize result = write ( fd_sender_, buffer,
sizeof(data) - n_written);
if(result < 0)
{
if(errno == EINTR)
continue;
warn_failed_pipe_io("write", errno);
return;
}
n_written += result;
}
while(n_written < sizeof(data));
#endif
}
bool DispatchNotifier::pipe_io_handler(Glib::IOCondition)
{
#ifdef GTKMM_WIN32
DispatchNotifyData* data;
while ( (data = notify_queue_.get()) )
{
DispatchNotifyData local_data = *data;
delete data;
g_return_val_if_fail(local_data.tag == 0xdeadbeef, true);
g_return_val_if_fail(local_data.notifier == this, true);
// Actually, we wouldn't need the try/catch block because the Glib::Source
// C callback already does it for us. However, we do it anyway because the
// default return value is 'false', which is not what we want.
try
{
local_data.dispatcher->signal_();
}
catch(...)
{
Glib::exception_handlers_invoke();
}
}
#else
DispatchNotifyData data = { 0, 0, 0, };
gsize n_read = 0;
// Look at this code -- we're prepared for the worst. Hah!
do
{
void * const buffer = reinterpret_cast<guint8*>(&data) + n_read;
const gssize result = read(fd_receiver_, buffer, sizeof(data) - n_read);
if(result < 0)
{
if(errno == EINTR)
continue;
warn_failed_pipe_io("read", errno);
return true;
}
n_read += result;
}
while(n_read < sizeof(data));
g_return_val_if_fail(data.tag == 0xdeadbeef, true);
g_return_val_if_fail(data.notifier == this, true);
// Actually, we wouldn't need the try/catch block because the Glib::Source
// C callback already does it for us. However, we do it anyway because the
// default return value is 'false', which is not what we want.
try
{
data.dispatcher->signal_();
}
catch(...)
{
Glib::exception_handlers_invoke();
}
#endif
return true;
}
/**** Glib::Dispatcher *****************************************************/
Dispatcher::Dispatcher()
:
signal_ (),
notifier_ (DispatchNotifier::reference_instance(MainContext::get_default()))
{}
Dispatcher::Dispatcher(const Glib::RefPtr<MainContext>& context)
:
signal_ (),
notifier_ (DispatchNotifier::reference_instance(context))
{}
Dispatcher::~Dispatcher()
{
DispatchNotifier::unreference_instance(notifier_);
}
void Dispatcher::emit()
{
notifier_->send_notification(this);
}
void Dispatcher::operator()()
{
emit();
}
SigC::Connection Dispatcher::connect(const SigC::Slot0<void>& slot)
{
return signal_.connect(slot);
}
} // namespace Glib
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]