Thread safety for python. Closes #1063.

This commit is contained in:
Hermann Kraus 2012-02-08 00:40:02 +01:00
parent 8da30e7433
commit 6121e47f50
3 changed files with 131 additions and 35 deletions

View file

@ -86,6 +86,7 @@ void export_label_collision_detector();
#include <mapnik/scale_denominator.hpp>
#include "python_grid_utils.hpp"
#include "mapnik_value_converter.hpp"
#include "mapnik_threads.hpp"
#include "python_optional.hpp"
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
@ -93,13 +94,17 @@ void export_label_collision_detector();
static Pycairo_CAPI_t *Pycairo_CAPI;
#endif
using mapnik::python_thread;
bool python_thread::thread_support = true;
boost::thread_specific_ptr<PyThreadState> python_thread::state;
void render(const mapnik::Map& map,
mapnik::image_32& image,
double scale_factor = 1.0,
unsigned offset_x = 0u,
unsigned offset_y = 0u)
{
// Py_BEGIN_ALLOW_THREADS
python_thread::unblock();
try
{
mapnik::agg_renderer<mapnik::image_32> ren(map,image,scale_factor,offset_x, offset_y);
@ -107,10 +112,10 @@ void render(const mapnik::Map& map,
}
catch (...)
{
// Py_BLOCK_THREADS
python_thread::block();
throw;
}
// Py_END_ALLOW_THREADS
python_thread::block();
}
void render_with_detector(
@ -121,7 +126,7 @@ void render_with_detector(
unsigned offset_x = 0u,
unsigned offset_y = 0u)
{
Py_BEGIN_ALLOW_THREADS
python_thread::unblock();
try
{
mapnik::agg_renderer<mapnik::image_32> ren(map,image,detector);
@ -129,10 +134,10 @@ void render_with_detector(
}
catch (...)
{
Py_BLOCK_THREADS
python_thread::block();
throw;
}
Py_END_ALLOW_THREADS
python_thread::block();
}
void render_layer2(const mapnik::Map& map,
@ -148,7 +153,7 @@ void render_layer2(const mapnik::Map& map,
throw std::runtime_error(s.str());
}
Py_BEGIN_ALLOW_THREADS
python_thread::unblock();
try
{
mapnik::layer const& layer = layers[layer_idx];
@ -158,10 +163,10 @@ void render_layer2(const mapnik::Map& map,
}
catch (...)
{
Py_BLOCK_THREADS
python_thread::block();
throw;
}
Py_END_ALLOW_THREADS
python_thread::block();
}
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
@ -171,7 +176,7 @@ void render3(const mapnik::Map& map,
unsigned offset_x = 0,
unsigned offset_y = 0)
{
Py_BEGIN_ALLOW_THREADS
python_thread::unblock();
try
{
Cairo::RefPtr<Cairo::Surface> s(new Cairo::Surface(surface->surface));
@ -180,15 +185,15 @@ void render3(const mapnik::Map& map,
}
catch (...)
{
Py_BLOCK_THREADS
python_thread::block();
throw;
}
Py_END_ALLOW_THREADS
python_thread::block();
}
void render4(const mapnik::Map& map, PycairoSurface* surface)
{
Py_BEGIN_ALLOW_THREADS
python_thread::unblock();
try
{
Cairo::RefPtr<Cairo::Surface> s(new Cairo::Surface(surface->surface));
@ -197,10 +202,10 @@ void render4(const mapnik::Map& map, PycairoSurface* surface)
}
catch (...)
{
Py_BLOCK_THREADS
python_thread::block();
throw;
}
Py_END_ALLOW_THREADS
python_thread::block();
}
void render5(const mapnik::Map& map,
@ -208,7 +213,7 @@ void render5(const mapnik::Map& map,
unsigned offset_x = 0,
unsigned offset_y = 0)
{
Py_BEGIN_ALLOW_THREADS
python_thread::unblock();
try
{
Cairo::RefPtr<Cairo::Context> c(new Cairo::Context(context->ctx));
@ -217,15 +222,15 @@ void render5(const mapnik::Map& map,
}
catch (...)
{
Py_BLOCK_THREADS
python_thread::block();
throw;
}
Py_END_ALLOW_THREADS
python_thread::block();
}
void render6(const mapnik::Map& map, PycairoContext* context)
{
Py_BEGIN_ALLOW_THREADS
python_thread::unblock();
try
{
Cairo::RefPtr<Cairo::Context> c(new Cairo::Context(context->ctx));
@ -234,10 +239,10 @@ void render6(const mapnik::Map& map, PycairoContext* context)
}
catch (...)
{
Py_BLOCK_THREADS
python_thread::block();
throw;
}
Py_END_ALLOW_THREADS
python_thread::block();
}
#endif

View file

@ -2,7 +2,7 @@
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon
* Copyright (C) 2012 Artem Pavlenko, Jean-Francois Doyon
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,14 +19,13 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
#include <boost/python.hpp>
#include <mapnik/text_placements.hpp>
#include "mapnik_enumeration.hpp"
#include <mapnik/expression_string.hpp>
#include <mapnik/text_symbolizer.hpp>
#include "mapnik_threads.hpp"
using namespace mapnik;
@ -77,12 +76,9 @@ struct NodeWrap: formating::node, wrapper<formating::node>
void apply(char_properties const& p, Feature const& feature, processed_text &output) const
{
override o = this->get_override("apply");
#ifdef MAPNIK_DEBUG
if (!o) {
std::cerr << "WARNING: No apply function found in FormatingNode!\n";
}
#endif
python_thread::block();
o(ptr(&p), ptr(&feature), ptr(&output));
python_thread::unblock();
}
virtual void add_expressions(expression_set &output) const
@ -90,7 +86,9 @@ struct NodeWrap: formating::node, wrapper<formating::node>
override o = this->get_override("add_expressions");
if (o)
{
python_thread::block();
o(ptr(&output));
python_thread::unblock();
} else
{
formating::node::add_expressions(output);
@ -113,9 +111,11 @@ struct TextNodeWrap: formating::text_node, wrapper<formating::text_node>
virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const
{
if(override func_apply = this->get_override("apply"))
if(override o = this->get_override("apply"))
{
func_apply(ptr(&p), ptr(&feature), ptr(&output));
python_thread::block();
o(ptr(&p), ptr(&feature), ptr(&output));
python_thread::unblock();
}
else
{
@ -133,9 +133,11 @@ struct FormatNodeWrap: formating::format_node, wrapper<formating::format_node>
{
virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const
{
if(override func_apply = this->get_override("apply"))
if(override o = this->get_override("apply"))
{
func_apply(ptr(&p), ptr(&feature), ptr(&output));
python_thread::block();
o(ptr(&p), ptr(&feature), ptr(&output));
python_thread::unblock();
}
else
{
@ -154,7 +156,11 @@ struct TextPlacementsWrap: text_placements, wrapper<text_placements>
text_placement_info_ptr get_placement_info(double scale_factor_, dimension_type dim,
bool has_dimensions_) const
{
return this->get_override("get_placement_info")();
override o = this->get_override("get_placement_info");
python_thread::block();
text_placement_info_ptr result = o();
python_thread::unblock();
return result;
}
};
@ -169,7 +175,11 @@ struct TextPlacementInfoWrap: text_placement_info, wrapper<text_placement_info>
bool next()
{
return this->get_override("next")();
override o = this->get_override("next");
python_thread::block();
bool result = o();
python_thread::unblock();
return result;
}
};

View file

@ -0,0 +1,81 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 Artem Pavlenko, Jean-Francois Doyon
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_THREADS_HPP
#define MAPNIK_THREADS_HPP
#include <boost/thread.hpp>
#include <boost/python.hpp>
namespace mapnik {
class python_thread
{
/* Docs:
http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock
*/
public:
static void unblock()
{
#ifdef MAPNIK_DEBUG
if (state.get())
{
std::cerr << "ERROR: Python threads are already unblocked. "
"Unblocking again will loose the current state and "
"might crash later. Aborting!\n";
abort(); //This is a serious error and can't be handled in any other sane way
}
#endif
PyThreadState *_save = 0; //Name defined by python
Py_UNBLOCK_THREADS;
state.reset(_save);
#ifdef MAPNIK_DEBUG
if (!_save) {
thread_support = false;
}
#endif
}
static void block()
{
#ifdef MAPNIK_DEBUG
if (thread_support && !state.get())
{
std::cerr << "ERROR: Trying to restore python thread state, "
"but no state is saved. Can't continue and also "
"can't raise an exception because the python "
"interpreter might be non-function. Aborting!\n";
abort();
}
#endif
PyThreadState *_save = state.release(); //Name defined by python
Py_BLOCK_THREADS;
}
private:
static boost::thread_specific_ptr<PyThreadState> state;
#ifdef MAPNIK_DEBUG
static bool thread_support;
#endif
};
} //namespace
#endif // MAPNIK_THREADS_HPP