Thread safety for python. Closes #1063.
This commit is contained in:
parent
8da30e7433
commit
6121e47f50
3 changed files with 131 additions and 35 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
81
bindings/python/mapnik_threads.hpp
Normal file
81
bindings/python/mapnik_threads.hpp
Normal 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
|
Loading…
Add table
Reference in a new issue