Skip to content

Commit

Permalink
Add support for registering interruption-checking callback.
Browse files Browse the repository at this point in the history
This is to enhance flexibility of the interruption request model.

git-svn-id: http://svn.osgeo.org/geos/trunk@3666 5242fede-7e19-0410-aef8-94bd7d2200fb
  • Loading branch information
Sandro Santilli committed Jun 7, 2012
1 parent e4d258c commit 6819ea6
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 0 deletions.
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Changes in 3.4.0
????-??-??

- New things:
- Interruptibility API (C and C++)
- CAPI: GEOSNode (#496) - PHP: Geometry->node
- GeometryPrecisionReducer class
- BufferInputLineSimplifier header exposed (#548)
Expand Down
6 changes: 6 additions & 0 deletions capi/geos_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ finishGEOS ()
}
}

void
GEOS_interruptRegisterCallback(GEOSInterruptCallback* cb, void *cbarg)
{
geos::util::Interrupt::registerCallback(cb, cbarg);
}

void
GEOS_interruptRequest()
{
Expand Down
8 changes: 8 additions & 0 deletions capi/geos_c.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ extern void GEOS_DLL finishGEOS(void);
extern GEOSAllocator GEOS_DLL GEOS_setAllocator(GEOSAllocator nf);
extern GEOSDeallocator GEOS_DLL GEOS_setDeallocator(GEOSDeallocator nf);

/*
* Register an interruption checking callback
*
* The callback will be invoked _before_ checking for
* interruption, so can be used to request it.
*/
typedef void (GEOSInterruptCallback)(void *arg);
extern void GEOS_DLL GEOS_interruptRegisterCallback(GEOSInterruptCallback* cb, void *cbarg);
/* Request safe interruption of operations */
extern void GEOS_DLL GEOS_interruptRequest();
/* Cancel a pending interruption request */
Expand Down
35 changes: 35 additions & 0 deletions include/geos/util/Interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,64 @@ namespace util { // geos::util

#define GEOS_CHECK_FOR_INTERRUPTS() geos::util::Interrupt::process()

/** Used to manage interruption requests and callbacks */
class GEOS_DLL Interrupt {

public:

typedef void (Callback)(void *userdata);

/**
* Request interruption of operations
*
* Operations will be terminated by a GEOSInterrupt
* exception at first occasion.
*/
static void request() { requested = true; }

/** Cancel a pending interruption request */
static void cancel() { requested = false; }

/** Check if an interruption request is pending */
static bool check() { return requested; }

/** \brief
* Register a callback that will be invoked
* before checking for interruption requests.
*
* NOTE that interruption request checking may happen
* frequently so any callback would better be quick.
*
* The callback can be used to call Interrupt::request()
*
*/
static void registerCallback(Callback *cb, void *arg) { callback = cb; callback_arg = arg; }

/**
* Invoke the callback, if any. Process pending interruption, if any.
*
*/
static void process() {
if ( callback ) (*callback)(callback_arg);
if ( requested ) {
requested = false;
interrupt();
}
}

/* Perform the actual interruption (simply throw an exception) */
static void interrupt();

private:

/* Could these be portably stored in thread-specific space ? */

static bool requested;

static Callback *callback;

static void *callback_arg;

};


Expand Down
2 changes: 2 additions & 0 deletions src/util/Interrupt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ void
Interrupt::interrupt() { throw InterruptedException(); }

bool Interrupt::requested = false;
Interrupt::Callback *Interrupt::callback = 0;
void *Interrupt::callback_arg = 0;

} // namespace geos::util
} // namespace geos
Expand Down
1 change: 1 addition & 0 deletions tests/unit/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ geos_unit_SOURCES = \
capi/GEOSContainsTest.cpp \
capi/GEOSConvexHullTest.cpp \
capi/GEOSDistanceTest.cpp \
capi/GEOSInterruptTest.cpp \
capi/GEOSIntersectsTest.cpp \
capi/GEOSWithinTest.cpp \
capi/GEOSSimplifyTest.cpp \
Expand Down
125 changes: 125 additions & 0 deletions tests/unit/capi/GEOSInterruptTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//
// Test Suite for C-API custom allocators

#include <tut.hpp>
// geos
#include <geos_c.h>
// std
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <memory>

namespace tut
{
//
// Test Group
//

// Common data used in test cases.
struct test_capiinterrupt_data
{
static int alloc_count;
static int dealloc_count;

static void notice(const char *fmt, ...)
{
std::fprintf( stdout, "NOTICE: ");

va_list ap;
va_start(ap, fmt);
std::vfprintf(stdout, fmt, ap);
va_end(ap);

std::fprintf(stdout, "\n");
}

test_capiinterrupt_data()
{
}

~test_capiinterrupt_data()
{
}

static void interruptNow(void *)
{
GEOS_interruptRequest();
}

static void countCalls(void *arg)
{
int* numcalls = reinterpret_cast<int*>(arg);
++(*numcalls);
}

};

int test_capiinterrupt_data::alloc_count = 0;
int test_capiinterrupt_data::dealloc_count = 0;

typedef test_group<test_capiinterrupt_data> group;
typedef group::object object;

group test_capiinterrupt_group("capi::GEOSInterrupt");

//
// Test Cases
//

/// Test interrupt callback being called
template<>
template<>
void object::test<1>()
{
int numcalls = 0;
GEOS_interruptRegisterCallback(countCalls, &numcalls);

initGEOS(notice, notice);

ensure_equals(numcalls, 0);

GEOSGeometry *geom1 = GEOSGeomFromWKT("LINESTRING(0 0, 1 0)");

ensure("GEOSGeomFromWKT failed", 0 != geom1);

GEOSGeometry *geom2 = GEOSBuffer(geom1, 1, 8);

ensure("GEOSBufferWithStyle failed", 0 != geom2);

ensure("interrupt callback never called", numcalls > 0);

GEOSGeom_destroy(geom1);
GEOSGeom_destroy(geom2);

finishGEOS();
}

/// Test interrupting from callback
template<>
template<>
void object::test<2>()
{
initGEOS(notice, notice);

GEOS_interruptRegisterCallback(0, 0); /* unregister */

GEOSGeometry *geom1 = GEOSGeomFromWKT("LINESTRING(0 0, 1 0)");

ensure("GEOSGeomFromWKT failed", 0 != geom1);

GEOS_interruptRegisterCallback(interruptNow, 0);
bool interrupted = false;
GEOSGeometry *geom2 = GEOSBuffer(geom1, 1, 8);
ensure("GEOSBuffer wasn't interrupted", 0 == geom2);

// TODO: check the actual exception ? (sent to notice() callback)

GEOSGeom_destroy(geom1);

finishGEOS();
}


} // namespace tut

0 comments on commit 6819ea6

Please sign in to comment.