/* Copyright (C) 2013 The glibmm Development Team
*
* This file is part of glibmm.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*/
#include
#include
#include
#include // EXIT_SUCCESS, EXIT_FAILURE
namespace
{
enum InvokeStatus
{
NOT_INVOKED,
INVOKED_IN_RIGHT_THREAD,
INVOKED_IN_WRONG_THREAD
};
InvokeStatus invoked_in_thread[2] = { NOT_INVOKED, NOT_INVOKED };
void quit_loop(const Glib::RefPtr& mainloop)
{
mainloop->quit();
}
bool mark_and_quit(const std::thread::id& expected_thread_id,
int thread_nr, const Glib::RefPtr& mainloop)
{
invoked_in_thread[thread_nr] =
(std::this_thread::get_id() == expected_thread_id) ?
INVOKED_IN_RIGHT_THREAD : INVOKED_IN_WRONG_THREAD;
mainloop->get_context()->signal_idle().connect_once(
sigc::bind(sigc::ptr_fun(quit_loop), mainloop));
return false;
}
void thread_function(const std::thread::id& first_thread_id,
const Glib::RefPtr& first_mainloop)
{
auto second_context = Glib::MainContext::create();
auto second_mainloop = Glib::MainLoop::create(second_context);
// Show how Glib::MainContext::invoke() can be used for calling a function,
// possibly executed in another thread.
Glib::MainContext::get_default()->invoke(sigc::bind(sigc::ptr_fun(mark_and_quit),
first_thread_id, 0, first_mainloop));
// If this thread owns second_context, invoke() will call mark_and_quit() directly.
bool is_owner = second_context->acquire();
second_context->invoke(sigc::bind(sigc::ptr_fun(mark_and_quit),
std::this_thread::get_id(), 1, second_mainloop));
if (is_owner)
second_context->release();
// Start the second main loop.
second_mainloop->run();
}
} // anonymous namespace
int main(int, char**)
{
Glib::init();
auto first_mainloop = Glib::MainLoop::create();
// This thread shall be the owner of the default main context, when
// thread_function() calls mark_and_quit() via Glib::MainContext::invoke(),
// or else both calls to mark_and_quit() will execute in thread_function()'s
// thread. Glib::MainLoop::run() acquires ownership, but that may be too late.
bool is_owner = Glib::MainContext::get_default()->acquire();
// Create a second thread.
const std::thread::id first_thread_id = std::this_thread::get_id();
std::thread second_thread(&thread_function, first_thread_id, first_mainloop);
// Start the first main loop.
first_mainloop->run();
// Wait until the second thread has finished.
second_thread.join();
if (is_owner)
Glib::MainContext::get_default()->release();
if (invoked_in_thread[0] == INVOKED_IN_RIGHT_THREAD &&
invoked_in_thread[1] == INVOKED_IN_RIGHT_THREAD)
return EXIT_SUCCESS;
const char* N[2] = { "first", "second" };
for (int i = 0; i < 2; ++i)
{
switch (invoked_in_thread[i])
{
case INVOKED_IN_RIGHT_THREAD:
break;
case NOT_INVOKED:
std::cout << "Function that should be invoked in " << N[i]
<< " thread was not called." << std::endl;
break;
case INVOKED_IN_WRONG_THREAD:
std::cout << "Function that should be invoked in " << N[i]
<< " thread was called in another thread." << std::endl;
break;
default:
std::cout << "Unknown value: invoked_in_thread[" << i << "]="
<< invoked_in_thread[i] << std::endl;
break;
}
}
return EXIT_FAILURE;
}