diff --git a/build_geometry.cpp b/build_geometry.cpp index bb160abd2..46a6550cd 100644 --- a/build_geometry.cpp +++ b/build_geometry.cpp @@ -21,10 +21,15 @@ */ #include +#include #include +#include #include #include #include +#include +#include +#include #if defined(__CYGWIN__) #define GEOS_INLINE @@ -70,11 +75,31 @@ using namespace geos; typedef std::auto_ptr geom_ptr; -static std::vector wkts; -static std::vector areas; +//static std::vector wkts; +//static std::vector areas; + +struct geometry_ctx { + std::vector *wkts; + std::vector *areas; +}; static int excludepoly = 0; +void * init_geometry_ctx() { + struct geometry_ctx * ctx = (struct geometry_ctx *)malloc(sizeof(geometry_ctx)); + ctx->wkts = new std::vector; + ctx->areas = new std::vector; + clear_wkts(ctx); + return ctx; +} + +void close_geometry_ctx(void * ctx_p) { + struct geometry_ctx * ctx = (struct geometry_ctx *)ctx_p; + delete ctx->wkts; + delete ctx->areas; + free(ctx); +} + char *get_wkt_simple(osmNode *nodes, int count, int polygon) { GeometryFactory gf; std::auto_ptr coords(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2)); @@ -125,26 +150,27 @@ char *get_wkt_simple(osmNode *nodes, int count, int polygon) { // helper method to add the WKT for a geometry to the // global wkts list - used primarily for polygons. -void add_wkt(geom_ptr &geom, double area) { +static void add_wkt(struct geometry_ctx * ctx, geom_ptr &geom, double area) { WKTWriter wktw; std::string wkt = wktw.write(geom.get()); - wkts.push_back(wkt); - areas.push_back(area); + ctx->wkts->push_back(wkt); + ctx->areas->push_back(area); } // helper method to add the WKT for a line built from a // coordinate sequence to the global wkts list. -void add_wkt_line(GeometryFactory &gf, std::auto_ptr &segment) { +static void add_wkt_line(struct geometry_ctx * ctx, GeometryFactory &gf, std::auto_ptr &segment) { WKTWriter wktw; geom_ptr geom = geom_ptr(gf.createLineString(segment.release())); std::string wkt = wktw.write(geom.get()); - wkts.push_back(wkt); - areas.push_back(0); + ctx->wkts->push_back(wkt); + ctx->areas->push_back(0); segment.reset(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2)); } -size_t get_wkt_split(osmNode *nodes, int count, int polygon, double split_at) { +size_t get_wkt_split(void * ctx_p, osmNode *nodes, int count, int polygon, double split_at) { GeometryFactory gf; + struct geometry_ctx * ctx = (struct geometry_ctx *)ctx_p; std::auto_ptr coords(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2)); double area; size_t wkt_size = 0; @@ -171,7 +197,7 @@ size_t get_wkt_split(osmNode *nodes, int count, int polygon, double split_at) { } geom->normalize(); // Fix direction of ring area = geom->getArea(); - add_wkt(geom, area); + add_wkt(ctx, geom, area); } else { if (coords->getSize() < 2) @@ -198,7 +224,7 @@ size_t get_wkt_split(osmNode *nodes, int count, int polygon, double split_at) { const Coordinate interpolated(frac * (this_pt.x - prev_pt.x) + prev_pt.x, frac * (this_pt.y - prev_pt.y) + prev_pt.y); segment->add(interpolated); - add_wkt_line(gf, segment); + add_wkt_line(ctx, gf, segment); segment->add(interpolated); } // reset the distance based on the final splitting point for @@ -216,13 +242,13 @@ size_t get_wkt_split(osmNode *nodes, int count, int polygon, double split_at) { // on the last iteration, close out the line. if (i == coords->getSize()-1) { - add_wkt_line(gf, segment); + add_wkt_line(ctx, gf, segment); } } } // ensure the number of wkts in the global list is accurate. - wkt_size = wkts.size(); + wkt_size = ctx->wkts->size(); } catch (std::bad_alloc) { @@ -239,26 +265,29 @@ size_t get_wkt_split(osmNode *nodes, int count, int polygon, double split_at) { } -char * get_wkt(size_t index) +char * get_wkt(void * ctx_p, size_t index) { + struct geometry_ctx * ctx = (struct geometry_ctx *)ctx_p; // return wkts[index].c_str(); char *result; - result = (char*) std::malloc( wkts[index].length() + 1); + result = (char*) std::malloc( (*(ctx->wkts))[index].length() + 1); // At least give some idea of why we about to seg fault - if (!result) std::cerr << std::endl << "Unable to allocate memory: " << (wkts[index].length() + 1) << std::endl; - std::strcpy(result, wkts[index].c_str()); + if (!result) std::cerr << std::endl << "Unable to allocate memory: " << ((*(ctx->wkts))[index].length() + 1) << std::endl; + std::strcpy(result, (*(ctx->wkts))[index].c_str()); return result; } -double get_area(size_t index) +double get_area(void * ctx_p, size_t index) { - return areas[index]; + struct geometry_ctx * ctx = (struct geometry_ctx *)ctx_p; + return (*(ctx->areas))[index]; } -void clear_wkts() +void clear_wkts(void * ctx_p) { - wkts.clear(); - areas.clear(); + struct geometry_ctx * ctx = (struct geometry_ctx *)ctx_p; + ctx->wkts->clear(); + ctx->areas->clear(); } static int coords2nodes(CoordinateSequence * coords, struct osmNode ** nodes) { @@ -359,7 +388,8 @@ static int polygondata_comparearea(const void* vp1, const void* vp2) return 1; } -size_t build_geometry(osmid_t osm_id, struct osmNode **xnodes, int *xcount, int make_polygon, int enable_multi, double split_at) { +size_t build_geometry(void * ctx_p, osmid_t osm_id, struct osmNode **xnodes, int *xcount, int make_polygon, int enable_multi, double split_at) { + struct geometry_ctx * ctx = (struct geometry_ctx *)ctx_p; size_t wkt_size = 0; std::auto_ptr > lines(new std::vector); GeometryFactory gf; @@ -428,8 +458,8 @@ size_t build_geometry(osmid_t osm_id, struct osmNode **xnodes, int *xcount, int if ((distance >= split_at) || (i == pline->getNumPoints()-1)) { geom = geom_ptr(gf.createLineString(segment.release())); std::string wkt = writer.write(geom.get()); - wkts.push_back(wkt); - areas.push_back(0); + ctx->wkts->push_back(wkt); + ctx->areas->push_back(0); wkt_size++; distance=0; segment = std::auto_ptr(gf.getCoordinateSequenceFactory()->create((size_t)0, (size_t)2)); @@ -545,8 +575,8 @@ size_t build_geometry(osmid_t osm_id, struct osmNode **xnodes, int *xcount, int if ((excludepoly == 0) || (multipoly->isValid())) { std::string text = writer.write(multipoly.get()); - wkts.push_back(text); - areas.push_back(multipoly->getArea()); + ctx->wkts->push_back(text); + ctx->areas->push_back(multipoly->getArea()); wkt_size++; } } @@ -562,8 +592,8 @@ size_t build_geometry(osmid_t osm_id, struct osmNode **xnodes, int *xcount, int if ((excludepoly == 0) || (poly->isValid())) { std::string text = writer.write(poly); - wkts.push_back(text); - areas.push_back(poly->getArea()); + ctx->wkts->push_back(text); + ctx->areas->push_back(poly->getArea()); wkt_size++; } delete(poly); diff --git a/build_geometry.h b/build_geometry.h index 299431faf..a16d596cb 100644 --- a/build_geometry.h +++ b/build_geometry.h @@ -29,15 +29,17 @@ extern "C" { #include "osmtypes.h" +void * init_geometry_ctx(); +void close_geometry_ctx(void * ctx); int parse_wkt(const char * wkt, struct osmNode *** xnodes, int ** xcount, int * polygon); char *get_wkt_simple(struct osmNode *, int count, int polygon); -size_t get_wkt_split(struct osmNode *, int count, int polygon, double split_at); +size_t get_wkt_split(void * ctx_p, struct osmNode *, int count, int polygon, double split_at); -char* get_wkt(size_t index); -double get_area(size_t index); -size_t build_geometry(osmid_t osm_id, struct osmNode **xnodes, int *xcount, int make_polygon, int enable_multi, double split_at); -void clear_wkts(); +char* get_wkt(void * ctx_p, size_t index); +double get_area(void * ctx_p, size_t index); +size_t build_geometry(void * ctx_p, osmid_t osm_id, struct osmNode **xnodes, int *xcount, int make_polygon, int enable_multi, double split_at); +void clear_wkts(void * ctx_p); void exclude_broken_polygon (); #ifdef __cplusplus diff --git a/expire-tiles.c b/expire-tiles.c index f75352bbb..4ced59be9 100644 --- a/expire-tiles.c +++ b/expire-tiles.c @@ -104,6 +104,7 @@ static int _mark_tile(struct tile ** tree, int x, int y, int zoom, int this_zoom * Returns the number of subtiles which have all their children marked as dirty. */ static int mark_tile(struct tile ** tree_head, int x, int y, int zoom) { + //TODO: probably needs to be wrapped in a lock return _mark_tile(tree_head, x, y, zoom, 0); } diff --git a/keyvals.c b/keyvals.c index b14bc5fe1..164e3fefd 100644 --- a/keyvals.c +++ b/keyvals.c @@ -4,7 +4,7 @@ * tags, segment lists etc * */ -#define USE_TREE +//#define USE_TREE #include #include @@ -12,10 +12,15 @@ #include #include #include "keyvals.h" - -#ifdef USE_TREE #include "text-tree.h" -#endif + +static int deduplicate_strings; + +void setDeduplicateStrings(int dedup) { + if (dedup) fprintf(stderr, "Deduplicating tag strings to save memory.\n"); + else fprintf(stderr, "Storing tag strings verbatim.\n"); + deduplicate_strings = dedup; +} void initList(struct keyval *head) { @@ -33,13 +38,13 @@ void freeItem(struct keyval *p) if (!p) return; -#ifdef USE_TREE - text_release(tree_ctx, p->key); - text_release(tree_ctx, p->value); -#else - free(p->key); - free(p->value); -#endif + if (deduplicate_strings) { + text_release(tree_ctx, p->key); + text_release(tree_ctx, p->value); + } else { + free(p->key); + free(p->value); + } free(p); } @@ -172,13 +177,13 @@ void updateItem(struct keyval *head, const char *name, const char *value) item = head->next; while(item != head) { if (!strcmp(item->key, name)) { -#ifdef USE_TREE - text_release(tree_ctx, item->value); - item->value = (char *)text_get(tree_ctx,value); -#else - free(item->value); - item->value = strdup(value); -#endif + if (deduplicate_strings) { + text_release(tree_ctx, item->value); + item->value = (char *)text_get(tree_ctx,value); + } else { + free(item->value); + item->value = strdup(value); + } return; } item = item->next; @@ -244,29 +249,20 @@ int addItem(struct keyval *head, const char *name, const char *value, int noDupe return 2; } -#ifdef USE_TREE - item->key = (char *)text_get(tree_ctx,name); - item->value = (char *)text_get(tree_ctx,value); -#else - item->key = strdup(name); - item->value = strdup(value); -#endif + if (deduplicate_strings) { + item->key = (char *)text_get(tree_ctx,name); + item->value = (char *)text_get(tree_ctx,value); + } else { + item->key = strdup(name); + item->value = strdup(value); + } item->has_column=0; - -#if 1 /* Add to head */ item->next = head->next; item->prev = head; head->next->prev = item; head->next = item; -#else - /* Add to tail */ - item->prev = head->prev; - item->next = head; - head->prev->next = item; - head->prev = item; -#endif return 0; } @@ -322,9 +318,12 @@ void keyval2hstore(char *hstring, struct keyval *tags) void keyval2hstore_manual(char *hstring, char *key, char *value) { - static char* str=NULL; - static size_t stlen=0; + char* str=NULL; + size_t stlen=0; size_t len; + + str = malloc(1024); + stlen = 1024; len=strlen(value); if (len>stlen) { @@ -341,6 +340,7 @@ void keyval2hstore_manual(char *hstring, char *key, char *value) escape4hstore(str,key); hstring+=sprintf(hstring,"\"%s\"=>",str); escape4hstore(str,value); - sprintf(hstring,"\"%s\"",str); + sprintf(hstring,"\"%s\"",str); + free(str); } diff --git a/keyvals.h b/keyvals.h index 71f77102e..693969e13 100644 --- a/keyvals.h +++ b/keyvals.h @@ -20,6 +20,8 @@ struct keyval { struct keyval *prev; }; +void setDeduplicateStrings(int dedup); + void initList(struct keyval *head); void freeItem(struct keyval *p); unsigned int countList(struct keyval *head); diff --git a/middle-pgsql.c b/middle-pgsql.c index 5c8744077..7d8b81762 100644 --- a/middle-pgsql.c +++ b/middle-pgsql.c @@ -66,12 +66,20 @@ struct table_desc { const char *analyze; const char *stop; const char *array_indexes; +}; +struct table_conn { + struct table_desc * desc; int copyMode; /* True if we are in copy mode */ int transactionMode; /* True if we are in an extended transaction */ PGconn *sql_conn; }; +struct mid_pg_thread_ctx { + struct table_conn * conn; + void * flat_nodes; +}; + static struct table_desc tables [] = { { /*table = t_node,*/ @@ -133,9 +141,8 @@ static struct table_desc tables [] = { }; static const int num_tables = sizeof(tables)/sizeof(tables[0]); -static struct table_desc *node_table = &tables[t_node]; -static struct table_desc *way_table = &tables[t_way]; -static struct table_desc *rel_table = &tables[t_rel]; + +static struct mid_pg_thread_ctx * global_ctx; static int Append; @@ -148,8 +155,12 @@ const struct output_options *out_options; #define HELPER_STATE_CONNECTED 2 #define HELPER_STATE_FAILED 3 -static int pgsql_connect(const struct output_options *options) { +static void * pgsql_connect(const struct output_options *options) { int i; + struct mid_pg_thread_ctx * thread_ctx = calloc(1,sizeof(struct mid_pg_thread_ctx)); + struct table_conn * tables_conn = calloc(num_tables, sizeof(struct table_conn)); + if (options->flat_node_cache_enabled) thread_ctx->flat_nodes = init_node_persistent_cache(options, 1); + thread_ctx->conn = tables_conn; /* We use a connection per table to enable the use of COPY */ for (i=0; iconn; int i; for (i=0; iflat_node_cache_enabled) { + shutdown_node_persistent_cache(thread_ctx->flat_nodes); + thread_ctx->flat_nodes = NULL; + } + free(thread_ctx->conn); + free(thread_ctx); } char *pgsql_store_nodes(osmid_t *nds, int nd_count) { - static char *buffer; - static int buflen; + char *buffer; + int buflen; char *ptr; int i, first; - if( buflen <= nd_count * 10 ) - { - buflen = ((nd_count * 10) | 4095) + 1; /* Round up to next page */ - buffer = realloc( buffer, buflen ); - } + buflen = ((nd_count * 10) | 4095) + 1; /* Round up to next page */ + buffer = malloc( buflen ); + _restart: ptr = buffer; @@ -272,8 +292,8 @@ static char *escape_tag( char *ptr, const char *in, int escape ) /* escape means we return '\N' for copy mode, otherwise we return just NULL */ char *pgsql_store_tags(struct keyval *tags, int escape) { - static char *buffer; - static int buflen; + char *buffer; + int buflen; char *ptr; struct keyval *i; @@ -283,16 +303,15 @@ char *pgsql_store_tags(struct keyval *tags, int escape) if( countlist == 0 ) { if( escape ) - return "\\N"; + return strdup("\\N"); //Needed, as the calling function will free the returned buffer; else return NULL; } - if( buflen <= countlist * 24 ) /* LE so 0 always matches */ - { - buflen = ((countlist * 24) | 4095) + 1; /* Round up to next page */ - buffer = realloc( buffer, buflen ); - } + + buflen = ((countlist * 24) | 4095) + 1; /* Round up to next page */ + buffer = malloc( buflen ); + _restart: ptr = buffer; @@ -401,7 +420,7 @@ static void pgsql_parse_nodes(const char *src, osmid_t *nds, int nd_count ) } } -static int pgsql_endCopy( struct table_desc *table) +static int pgsql_endCopy( struct table_conn *table) { PGresult *res; PGconn *sql_conn; @@ -411,13 +430,13 @@ static int pgsql_endCopy( struct table_desc *table) sql_conn = table->sql_conn; stop = PQputCopyEnd(sql_conn, NULL); if (stop != 1) { - fprintf(stderr, "COPY_END for %s failed: %s\n", table->copy, PQerrorMessage(sql_conn)); + fprintf(stderr, "COPY_END for %s failed: %s\n", table->desc->copy, PQerrorMessage(sql_conn)); exit_nicely(); } res = PQgetResult(sql_conn); if (PQresultStatus(res) != PGRES_COMMAND_OK) { - fprintf(stderr, "COPY_END for %s failed: %s\n", table->copy, PQerrorMessage(sql_conn)); + fprintf(stderr, "COPY_END for %s failed: %s\n", table->desc->copy, PQerrorMessage(sql_conn)); PQclear(res); exit_nicely(); } @@ -427,13 +446,15 @@ static int pgsql_endCopy( struct table_desc *table) return 0; } -static int pgsql_nodes_set(osmid_t id, double lat, double lon, struct keyval *tags) +static int pgsql_nodes_set(void * thread_ctxp, osmid_t id, double lat, double lon, struct keyval *tags) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; /* Four params: id, lat, lon, tags */ char *paramValues[4]; char *buffer; - if( node_table->copyMode ) + if( tables_conn[t_node].copyMode ) { char *tag_buf = pgsql_store_tags(tags,1); int length = strlen(tag_buf) + 64; @@ -445,7 +466,8 @@ static int pgsql_nodes_set(osmid_t id, double lat, double lon, struct keyval *ta if( snprintf( buffer, length, "%" PRIdOSMID "\t%.10f\t%.10f\t%s\n", id, lat, lon, tag_buf ) > (length-10) ) { fprintf( stderr, "buffer overflow node id %" PRIdOSMID "\n", id); return 1; } #endif - return pgsql_CopyData(__FUNCTION__, node_table->sql_conn, buffer); + if (tag_buf) free(tag_buf); + return pgsql_CopyData(__FUNCTION__, tables_conn[t_node].sql_conn, buffer); } buffer = alloca(64); paramValues[0] = buffer; @@ -458,27 +480,31 @@ static int pgsql_nodes_set(osmid_t id, double lat, double lon, struct keyval *ta sprintf( paramValues[2], "%.10f", lon ); #endif paramValues[3] = pgsql_store_tags(tags,0); - pgsql_execPrepared(node_table->sql_conn, "insert_node", 4, (const char * const *)paramValues, PGRES_COMMAND_OK); + pgsql_execPrepared(tables_conn[t_node].sql_conn, "insert_node", 4, (const char * const *)paramValues, PGRES_COMMAND_OK); + free(paramValues[3]); return 0; } -static int middle_nodes_set(osmid_t id, double lat, double lon, struct keyval *tags) { - ram_cache_nodes_set( id, lat, lon, tags ); +static int middle_nodes_set(void * thread_ctxp, osmid_t id, double lat, double lon, struct keyval *tags) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + ram_cache_nodes_set( thread_ctx, id, lat, lon, tags ); - return (out_options->flat_node_cache_enabled) ? persistent_cache_nodes_set(id, lat, lon) : pgsql_nodes_set(id, lat, lon, tags); + return (out_options->flat_node_cache_enabled) ? persistent_cache_nodes_set(thread_ctx->flat_nodes, id, lat, lon) : pgsql_nodes_set(thread_ctx, id, lat, lon, tags); } #if 0 -static int pgsql_nodes_get(struct osmNode *out, osmid_t id) +static int pgsql_nodes_get(void * thread_ctxp, struct osmNode *out, osmid_t id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; PGresult *res; char tmp[16]; char const *paramValues[1]; - PGconn *sql_conn = node_table->sql_conn; + PGconn *sql_conn = tables_conn[t_node].sql_conn; /* Make sure we're out of copy mode */ - pgsql_endCopy( node_table ); + pgsql_endCopy( &(tables_conn[t_node]) ); snprintf(tmp, sizeof(tmp), "%" PRIdOSMID, id); paramValues[0] = tmp; @@ -514,8 +540,10 @@ static int middle_nodes_get(struct osmNode *out, osmid_t id) /* This should be made more efficient by using an IN(ARRAY[]) construct */ -static int pgsql_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_count) +static int pgsql_nodes_get_list(void * thread_ctxp, struct osmNode *nodes, osmid_t *ndids, int nd_count) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char tmp[16]; char *tmp2; int count, countDB, countPG, i,j; @@ -524,7 +552,7 @@ static int pgsql_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_co char const *paramValues[1]; PGresult *res; - PGconn *sql_conn = node_table->sql_conn; + PGconn *sql_conn = tables_conn[t_node].sql_conn; count = 0; countDB = 0; @@ -552,8 +580,8 @@ static int pgsql_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_co free(tmp2); return count; /* All ids where in cache, so nothing more to do */ } - - pgsql_endCopy(node_table); + + pgsql_endCopy(&(tables_conn[t_node])); paramValues[0] = tmp2; res = pgsql_execPrepared(sql_conn, "get_node_list", 1, paramValues, PGRES_TUPLES_OK); @@ -620,51 +648,64 @@ static int pgsql_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_co return count; } -static int middle_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_count) +static int middle_nodes_get_list(void * thread_ctxp, struct osmNode *nodes, osmid_t *ndids, int nd_count) { - return (out_options->flat_node_cache_enabled) ? persistent_cache_nodes_get_list(nodes, ndids, nd_count) : pgsql_nodes_get_list(nodes, ndids, nd_count); + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + return (out_options->flat_node_cache_enabled) ? persistent_cache_nodes_get_list(thread_ctx->flat_nodes, nodes, ndids, nd_count) : pgsql_nodes_get_list(thread_ctx, nodes, ndids, nd_count); } -static int pgsql_nodes_delete(osmid_t osm_id) +static int pgsql_nodes_delete(void * thread_ctxp, osmid_t osm_id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char const *paramValues[1]; char buffer[64]; /* Make sure we're out of copy mode */ - pgsql_endCopy( node_table ); + pgsql_endCopy( &(tables_conn[t_node]) ); sprintf( buffer, "%" PRIdOSMID, osm_id ); paramValues[0] = buffer; - pgsql_execPrepared(node_table->sql_conn, "delete_node", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_node].sql_conn, "delete_node", 1, paramValues, PGRES_COMMAND_OK ); return 0; } -static int middle_nodes_delete(osmid_t osm_id) +static int middle_nodes_delete(void * thread_ctxp, osmid_t osm_id) { - return ((out_options->flat_node_cache_enabled) ? persistent_cache_nodes_set(osm_id, NAN, NAN) : pgsql_nodes_delete(osm_id)); + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + return ((out_options->flat_node_cache_enabled) ? persistent_cache_nodes_set(thread_ctx->flat_nodes, osm_id, NAN, NAN) : pgsql_nodes_delete(thread_ctx, osm_id)); } -static int pgsql_node_changed(osmid_t osm_id) +static int pgsql_node_changed(void * thread_ctxp, osmid_t osm_id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char const *paramValues[1]; char buffer[64]; /* Make sure we're out of copy mode */ - pgsql_endCopy( way_table ); - pgsql_endCopy( rel_table ); + pgsql_endCopy( &(tables_conn[t_way]) ); + pgsql_endCopy( &(tables_conn[t_rel]) ); sprintf( buffer, "%" PRIdOSMID, osm_id ); paramValues[0] = buffer; - pgsql_execPrepared(way_table->sql_conn, "node_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); - pgsql_execPrepared(rel_table->sql_conn, "node_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_way].sql_conn, "node_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_rel].sql_conn, "node_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); return 0; } -static int pgsql_ways_set(osmid_t way_id, osmid_t *nds, int nd_count, struct keyval *tags, int pending) +static int pgsql_ways_set(void * thread_ctxp, osmid_t way_id, osmid_t *nds, int nd_count, struct keyval *tags, int pending) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; /* Three params: id, nodes, tags, pending */ char *paramValues[4]; char *buffer; - if( way_table->copyMode ) + if ((tables[t_way].copy) && (tables_conn[t_way].copyMode == 0)) { + pgsql_exec(tables_conn[t_way].sql_conn, PGRES_COPY_IN, "%s", tables[t_way].copy); + tables_conn[t_way].copyMode = 1; + } + + if( tables_conn[t_way].copyMode ) { char *tag_buf = pgsql_store_tags(tags,1); char *node_buf = pgsql_store_nodes(nds, nd_count); @@ -673,7 +714,10 @@ static int pgsql_ways_set(osmid_t way_id, osmid_t *nds, int nd_count, struct key if( snprintf( buffer, length, "%" PRIdOSMID "\t%s\t%s\t%c\n", way_id, node_buf, tag_buf, pending?'t':'f' ) > (length-10) ) { fprintf( stderr, "buffer overflow way id %" PRIdOSMID "\n", way_id); return 1; } - return pgsql_CopyData(__FUNCTION__, way_table->sql_conn, buffer); + free(node_buf); + free(tag_buf); + + return pgsql_CopyData(__FUNCTION__, tables_conn[t_way].sql_conn, buffer); } buffer = alloca(64); paramValues[0] = buffer; @@ -681,22 +725,27 @@ static int pgsql_ways_set(osmid_t way_id, osmid_t *nds, int nd_count, struct key sprintf( paramValues[3], "%c", pending?'t':'f' ); paramValues[1] = pgsql_store_nodes(nds, nd_count); paramValues[2] = pgsql_store_tags(tags,0); - pgsql_execPrepared(way_table->sql_conn, "insert_way", 4, (const char * const *)paramValues, PGRES_COMMAND_OK); + pgsql_execPrepared(tables_conn[t_way].sql_conn, "insert_way", 4, (const char * const *)paramValues, PGRES_COMMAND_OK); + free(paramValues[1]); + free(paramValues[2]); + return 0; } /* Caller is responsible for freeing nodesptr & resetList(tags) */ -static int pgsql_ways_get(osmid_t id, struct keyval *tags, struct osmNode **nodes_ptr, int *count_ptr) +static int pgsql_ways_get(void * thread_ctxp, osmid_t id, struct keyval *tags, struct osmNode **nodes_ptr, int *count_ptr) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; PGresult *res; char tmp[16]; char const *paramValues[1]; - PGconn *sql_conn = way_table->sql_conn; + PGconn *sql_conn = tables_conn[t_way].sql_conn; int num_nodes; osmid_t *list; /* Make sure we're out of copy mode */ - pgsql_endCopy( way_table ); + pgsql_endCopy( &(tables_conn[t_way]) ); snprintf(tmp, sizeof(tmp), "%" PRIdOSMID, id); paramValues[0] = tmp; @@ -716,14 +765,15 @@ static int pgsql_ways_get(osmid_t id, struct keyval *tags, struct osmNode **node pgsql_parse_nodes( PQgetvalue(res, 0, 0), list, num_nodes); *count_ptr = out_options->flat_node_cache_enabled ? - persistent_cache_nodes_get_list(*nodes_ptr, list, num_nodes) : - pgsql_nodes_get_list( *nodes_ptr, list, num_nodes); + persistent_cache_nodes_get_list(thread_ctx->flat_nodes, *nodes_ptr, list, num_nodes) : + pgsql_nodes_get_list(thread_ctx, *nodes_ptr, list, num_nodes); PQclear(res); return 0; } -static int pgsql_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, struct keyval *tags, struct osmNode **nodes_ptr, int *count_ptr) { - +static int pgsql_ways_get_list(void * thread_ctxp, osmid_t *ids, int way_count, osmid_t **way_ids, struct keyval *tags, struct osmNode **nodes_ptr, int *count_ptr) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char tmp[16]; char *tmp2; int count, countPG, i, j; @@ -733,7 +783,7 @@ static int pgsql_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, s osmid_t *list; PGresult *res; - PGconn *sql_conn = way_table->sql_conn; + PGconn *sql_conn = tables_conn[t_way].sql_conn; *way_ids = malloc( sizeof(osmid_t) * (way_count + 1)); if (way_count == 0) return 0; @@ -749,9 +799,9 @@ static int pgsql_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, s } tmp2[strlen(tmp2) - 1] = '}'; /* replace last , with } to complete list of ids*/ - pgsql_endCopy(way_table); + pgsql_endCopy(&(tables_conn[t_way])); - paramValues[0] = tmp2; + paramValues[0] = tmp2; res = pgsql_execPrepared(sql_conn, "get_way_list", 1, paramValues, PGRES_TUPLES_OK); countPG = PQntuples(res); @@ -763,7 +813,6 @@ static int pgsql_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, s wayidspg[i] = strtoosmid(PQgetvalue(res, i, 0), NULL, 10); } - /* Match the list of ways coming from postgres in a different order back to the list of ways given by the caller */ count = 0; @@ -780,9 +829,8 @@ static int pgsql_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, s pgsql_parse_nodes( PQgetvalue(res, j, 1), list, num_nodes); count_ptr[count] = out_options->flat_node_cache_enabled ? - persistent_cache_nodes_get_list(nodes_ptr[count], list, num_nodes) : - pgsql_nodes_get_list( nodes_ptr[count], list, num_nodes); - + persistent_cache_nodes_get_list(thread_ctx->flat_nodes, nodes_ptr[count], list, num_nodes) : + pgsql_nodes_get_list(thread_ctx, nodes_ptr[count], list, num_nodes); count++; initList(&(tags[count])); } @@ -796,38 +844,41 @@ static int pgsql_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, s return count; } -static int pgsql_ways_done(osmid_t id) +static int pgsql_ways_done(void * thread_ctxp, osmid_t id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char tmp[16]; char const *paramValues[1]; - PGconn *sql_conn = way_table->sql_conn; + PGconn *sql_conn = tables_conn[t_way].sql_conn; /* Make sure we're out of copy mode */ - pgsql_endCopy( way_table ); + pgsql_endCopy( &(tables_conn[t_way]) ); snprintf(tmp, sizeof(tmp), "%" PRIdOSMID, id); paramValues[0] = tmp; - pgsql_execPrepared(sql_conn, "way_done", 1, paramValues, PGRES_COMMAND_OK); - return 0; } -static int pgsql_ways_delete(osmid_t osm_id) +static int pgsql_ways_delete(void * thread_ctxp, osmid_t osm_id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char const *paramValues[1]; char buffer[64]; /* Make sure we're out of copy mode */ - pgsql_endCopy( way_table ); + pgsql_endCopy( &(tables_conn[t_way]) ); sprintf( buffer, "%" PRIdOSMID, osm_id ); paramValues[0] = buffer; - pgsql_execPrepared(way_table->sql_conn, "delete_way", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_way].sql_conn, "delete_way", 1, paramValues, PGRES_COMMAND_OK ); return 0; } static void pgsql_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, struct osmNode *nodes, int count, int exists)) { + struct table_conn * tables_conn = global_ctx->conn; int noProcs = out_options->num_procs; int pid = 0; PGresult *res_ways; @@ -850,11 +901,9 @@ static void pgsql_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, fprintf(stderr, "\nGoing over pending ways...\n"); /* Make sure we're out of copy mode */ - pgsql_endCopy( way_table ); - - if (out_options->flat_node_cache_enabled) shutdown_node_persistent_cache(); + pgsql_endCopy( &(tables_conn[t_way]) ); - res_ways = pgsql_execPrepared(way_table->sql_conn, "pending_ways", 0, NULL, PGRES_TUPLES_OK); + res_ways = pgsql_execPrepared(tables_conn[t_way].sql_conn, "pending_ways", 0, NULL, PGRES_TUPLES_OK); fprintf(stderr, "\t%i ways are pending\n", PQntuples(res_ways)); @@ -886,7 +935,7 @@ static void pgsql_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, #endif if ((pid == 0) && (noProcs > 1)) { /* After forking, need to reconnect to the postgresql db */ - if ((pgsql_connect(out_options) != 0) || (out_options->out->connect(out_options, 1) != 0)) { + if (((global_ctx = (struct mid_pg_thread_ctx *)pgsql_connect(out_options)) == 0) || (out_options->out->connect(out_options, global_ctx, 1) != 0)) { #if HAVE_MMAP info[p].finished = HELPER_STATE_FAILED; #else @@ -898,23 +947,21 @@ static void pgsql_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, p = 0; } - if (out_options->flat_node_cache_enabled) init_node_persistent_cache(out_options,1); /* at this point we always want to be in append mode, to not delete and recreate the node cache file */ - /* Only start an extended transaction on the ways table, * which should cover the bulk of the update statements. * The nodes table should not be written to in this phase. * The relations table can't be wrapped in an extended - * transaction, as with prallel processing it may deadlock. + * transaction, as with parallel processing it may deadlock. * Updating a way will trigger an update of the pending status * on connected relations. This should not be as many updates, * so in combination with the synchronous_comit = off it should be fine. * */ - if (tables[t_way].start) { - pgsql_endCopy(&tables[t_way]); - pgsql_exec(tables[t_way].sql_conn, PGRES_COMMAND_OK, "%s", tables[t_way].start); - tables[t_way].transactionMode = 1; - } + /*if (tables[t_way].start) { + pgsql_endCopy(&tables_conn[t_way]); + pgsql_exec(tables_conn[t_way].sql_conn, PGRES_COMMAND_OK, "%s", tables_conn[t_way].desc->start); + tables_conn[t_way].transactionMode = 1; + }*/ #if HAVE_MMAP if (noProcs > 1) { @@ -1010,19 +1057,19 @@ static void pgsql_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, } initList(&tags); - if( pgsql_ways_get(id, &tags, &nodes, &nd_count) ) + if( pgsql_ways_get( global_ctx, id, &tags, &nodes, &nd_count) ) continue; callback(id, &tags, nodes, nd_count, exists); - pgsql_ways_done( id ); + pgsql_ways_done( global_ctx, id ); free(nodes); resetList(&tags); } - if (tables[t_way].stop && tables[t_way].transactionMode) { - pgsql_exec(tables[t_way].sql_conn, PGRES_COMMAND_OK, "%s", tables[t_way].stop); - tables[t_way].transactionMode = 0; + if (tables_conn[t_way].desc->stop && tables_conn[t_way].transactionMode) { + pgsql_exec(tables_conn[t_way].sql_conn, PGRES_COMMAND_OK, "%s", tables_conn[t_way].desc->stop); + tables_conn[t_way].transactionMode = 0; } time(&end); @@ -1040,9 +1087,8 @@ static void pgsql_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, fprintf(stderr, "\rProcess %i finished processing %i ways in %i sec\n", p, count, (int)(end - start)); if ((pid == 0) && (noProcs > 1)) { - pgsql_cleanup(); + pgsql_cleanup(global_ctx); out_options->out->close(1); - if (out_options->flat_node_cache_enabled) shutdown_node_persistent_cache(); exit(0); } else { for (p = 0; p < noProcs; p++) wait(NULL); @@ -1061,21 +1107,25 @@ static void pgsql_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, PQclear(res_ways); } -static int pgsql_way_changed(osmid_t osm_id) +static int pgsql_way_changed(void * thread_ctxp, osmid_t osm_id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char const *paramValues[1]; char buffer[64]; /* Make sure we're out of copy mode */ - pgsql_endCopy( rel_table ); + pgsql_endCopy( &(tables_conn[t_rel]) ); sprintf( buffer, "%" PRIdOSMID, osm_id ); paramValues[0] = buffer; - pgsql_execPrepared(rel_table->sql_conn, "way_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_rel].sql_conn, "way_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); return 0; } -static int pgsql_rels_set(osmid_t id, struct member *members, int member_count, struct keyval *tags) +static int pgsql_rels_set(void * thread_ctxp, osmid_t id, struct member *members, int member_count, struct keyval *tags) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; /* Params: id, way_off, rel_off, parts, members, tags */ char *paramValues[6]; char *buffer; @@ -1108,9 +1158,9 @@ static int pgsql_rels_set(osmid_t id, struct member *members, int member_count, memcpy( all_parts+all_count, way_parts, way_count*sizeof(osmid_t) ); all_count+=way_count; memcpy( all_parts+all_count, rel_parts, rel_count*sizeof(osmid_t) ); all_count+=rel_count; - if( rel_table->copyMode ) + if( tables_conn[t_rel].copyMode ) { - char *tag_buf = strdup(pgsql_store_tags(tags,1)); + char *tag_buf = pgsql_store_tags(tags,1); char *member_buf = pgsql_store_tags(&member_list,1); char *parts_buf = pgsql_store_nodes(all_parts, all_count); int length = strlen(member_buf) + strlen(tag_buf) + strlen(parts_buf) + 64; @@ -1119,8 +1169,10 @@ static int pgsql_rels_set(osmid_t id, struct member *members, int member_count, id, node_count, node_count+way_count, parts_buf, member_buf, tag_buf ) > (length-10) ) { fprintf( stderr, "buffer overflow relation id %" PRIdOSMID "\n", id); return 1; } free(tag_buf); + free(member_buf); + free(parts_buf); resetList(&member_list); - return pgsql_CopyData(__FUNCTION__, rel_table->sql_conn, buffer); + return pgsql_CopyData(__FUNCTION__, tables_conn[t_rel].sql_conn, buffer); } buffer = alloca(64); paramValues[0] = buffer; @@ -1129,23 +1181,27 @@ static int pgsql_rels_set(osmid_t id, struct member *members, int member_count, sprintf( paramValues[2], "%d", node_count+way_count ); paramValues[3] = pgsql_store_nodes(all_parts, all_count); paramValues[4] = pgsql_store_tags(&member_list,0); - if( paramValues[4] ) - paramValues[4] = strdup(paramValues[4]); paramValues[5] = pgsql_store_tags(tags,0); - pgsql_execPrepared(rel_table->sql_conn, "insert_rel", 6, (const char * const *)paramValues, PGRES_COMMAND_OK); + pgsql_execPrepared(tables_conn[t_rel].sql_conn, "insert_rel", 6, (const char * const *)paramValues, PGRES_COMMAND_OK); + if (paramValues[3]) + free(paramValues[3]); if( paramValues[4] ) free(paramValues[4]); + if( paramValues[5] ) + free(paramValues[5]); resetList(&member_list); return 0; } /* Caller is responsible for freeing members & resetList(tags) */ -static int pgsql_rels_get(osmid_t id, struct member **members, int *member_count, struct keyval *tags) +static int pgsql_rels_get(void * thread_ctxp, osmid_t id, struct member **members, int *member_count, struct keyval *tags) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; PGresult *res; char tmp[16]; char const *paramValues[1]; - PGconn *sql_conn = rel_table->sql_conn; + PGconn *sql_conn = tables_conn[t_rel].sql_conn; struct keyval member_temp; char tag; int num_members; @@ -1154,7 +1210,7 @@ static int pgsql_rels_get(osmid_t id, struct member **members, int *member_count struct keyval *item; /* Make sure we're out of copy mode */ - pgsql_endCopy( rel_table ); + pgsql_endCopy( &(tables_conn[t_rel]) ); snprintf(tmp, sizeof(tmp), "%" PRIdOSMID, id); paramValues[0] = tmp; @@ -1194,14 +1250,16 @@ static int pgsql_rels_get(osmid_t id, struct member **members, int *member_count return 0; } -static int pgsql_rels_done(osmid_t id) +static int pgsql_rels_done(void * thread_ctxp, osmid_t id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char tmp[16]; char const *paramValues[1]; - PGconn *sql_conn = rel_table->sql_conn; + PGconn *sql_conn = tables_conn[t_rel].sql_conn; /* Make sure we're out of copy mode */ - pgsql_endCopy( rel_table ); + pgsql_endCopy( &(tables_conn[t_rel]) ); snprintf(tmp, sizeof(tmp), "%" PRIdOSMID, id); paramValues[0] = tmp; @@ -1211,23 +1269,27 @@ static int pgsql_rels_done(osmid_t id) return 0; } -static int pgsql_rels_delete(osmid_t osm_id) +static int pgsql_rels_delete(void * thread_ctxp, osmid_t osm_id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char const *paramValues[1]; char buffer[64]; /* Make sure we're out of copy mode */ - pgsql_endCopy( way_table ); - pgsql_endCopy( rel_table ); + pgsql_endCopy( &(tables_conn[t_way]) ); + pgsql_endCopy( &(tables_conn[t_rel]) ); + pgsql_endCopy( &(tables_conn[t_way]) ); sprintf( buffer, "%" PRIdOSMID, osm_id ); paramValues[0] = buffer; - pgsql_execPrepared(way_table->sql_conn, "rel_delete_mark", 1, paramValues, PGRES_COMMAND_OK ); - pgsql_execPrepared(rel_table->sql_conn, "delete_rel", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_way].sql_conn, "rel_delete_mark", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_rel].sql_conn, "delete_rel", 1, paramValues, PGRES_COMMAND_OK ); return 0; } static void pgsql_iterate_relations(int (*callback)(osmid_t id, struct member *members, int member_count, struct keyval *tags, int exists)) { + struct table_conn * tables_conn = global_ctx->conn; PGresult *res_rels; int noProcs = out_options->num_procs; int pid; @@ -1250,11 +1312,9 @@ static void pgsql_iterate_relations(int (*callback)(osmid_t id, struct member *m fprintf(stderr, "\nGoing over pending relations...\n"); /* Make sure we're out of copy mode */ - pgsql_endCopy( rel_table ); + pgsql_endCopy( &(tables_conn[t_rel]) ); - if (out_options->flat_node_cache_enabled) shutdown_node_persistent_cache(); - - res_rels = pgsql_execPrepared(rel_table->sql_conn, "pending_rels", 0, NULL, PGRES_TUPLES_OK); + res_rels = pgsql_execPrepared(tables_conn[t_rel].sql_conn, "pending_rels", 0, NULL, PGRES_TUPLES_OK); fprintf(stderr, "\t%i relations are pending\n", PQntuples(res_rels)); @@ -1274,14 +1334,14 @@ static void pgsql_iterate_relations(int (*callback)(osmid_t id, struct member *m info[p].finished = HELPER_STATE_FAILED; fprintf(stderr,"WARNING: Failed to fork helper processes %i. Trying to recover.\n", p); #else - fprintf(stderr,"ERROR: Failed to fork helper processes. Can't recover! \n"); + fprintf(stderr,"ERROR: Failed to fork helper processes. Can't recover! \n"); exit_nicely(); #endif } } #endif if ((pid == 0) && (noProcs > 1)) { - if ((out_options->out->connect(out_options, 0) != 0) || (pgsql_connect(out_options) != 0)) { + if (((global_ctx = pgsql_connect(out_options)) == 0) || (out_options->out->connect(out_options, global_ctx, 0) != 0)) { #if HAVE_MMAP info[p].finished = HELPER_STATE_FAILED; #endif @@ -1291,7 +1351,6 @@ static void pgsql_iterate_relations(int (*callback)(osmid_t id, struct member *m p = 0; } - if (out_options->flat_node_cache_enabled) init_node_persistent_cache(out_options, 1); /* at this point we always want to be in append mode, to not delete and recreate the node cache file */ #if HAVE_MMAP if (noProcs > 1) { @@ -1383,11 +1442,11 @@ static void pgsql_iterate_relations(int (*callback)(osmid_t id, struct member *m } initList(&tags); - if( pgsql_rels_get(id, &members, &member_count, &tags) ) + if( pgsql_rels_get(global_ctx, id, &members, &member_count, &tags) ) continue; callback(id, members, member_count, &tags, exists); - pgsql_rels_done( id ); + pgsql_rels_done(global_ctx, id ); free(members); resetList(&tags); @@ -1407,9 +1466,8 @@ static void pgsql_iterate_relations(int (*callback)(osmid_t id, struct member *m fprintf(stderr, "\rProcess %i finished processing %i relations in %i sec\n", p, count, (int)(end - start)); if ((pid == 0) && (noProcs > 1)) { - pgsql_cleanup(); + pgsql_cleanup(global_ctx); out_options->out->close(0); - if (out_options->flat_node_cache_enabled) shutdown_node_persistent_cache(); exit(0); } else { for (p = 0; p < noProcs; p++) wait(NULL); @@ -1427,25 +1485,28 @@ static void pgsql_iterate_relations(int (*callback)(osmid_t id, struct member *m } -static int pgsql_rel_changed(osmid_t osm_id) +static int pgsql_rel_changed(void * thread_ctxp, osmid_t osm_id) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; char const *paramValues[1]; char buffer[64]; /* Make sure we're out of copy mode */ - pgsql_endCopy( rel_table ); + pgsql_endCopy( &(tables_conn[t_rel]) ); sprintf( buffer, "%" PRIdOSMID, osm_id ); paramValues[0] = buffer; - pgsql_execPrepared(rel_table->sql_conn, "rel_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); + pgsql_execPrepared(tables_conn[t_rel].sql_conn, "rel_changed_mark", 1, paramValues, PGRES_COMMAND_OK ); return 0; } static void pgsql_analyze(void) { + struct table_conn * tables_conn = global_ctx->conn; int i; for (i=0; iconn; int i; for (i=0; iconn = calloc(num_tables, sizeof(struct table_conn)); + init_node_ram_cache( options->alloc_chunkwise | ALLOC_LOSSY, options->cache, scale); - if (options->flat_node_cache_enabled) init_node_persistent_cache(options, options->append); + if (options->flat_node_cache_enabled) global_ctx->flat_nodes = init_node_persistent_cache(options, options->append); fprintf(stderr, "Mid: pgsql, scale=%d cache=%d\n", scale, options->cache); + /* We use a connection per table to enable the use of COPY */ for (i=0; iconn[i].sql_conn = sql_conn; + global_ctx->conn[i].desc = &(tables[i]); /* * To allow for parallelisation, the second phase (iterate_ways), cannot be run @@ -1671,9 +1739,9 @@ static int pgsql_start(const struct output_options *options) pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s", tables[i].name); } - if (tables[i].start) { + if (tables[i].start && (options->num_procs == 1)) { pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", tables[i].start); - tables[i].transactionMode = 1; + global_ctx->conn[i].transactionMode = 1; } if (dropcreate && tables[i].create) { @@ -1694,57 +1762,60 @@ static int pgsql_start(const struct output_options *options) if (tables[i].copy) { pgsql_exec(sql_conn, PGRES_COPY_IN, "%s", tables[i].copy); - tables[i].copyMode = 1; + global_ctx->conn[i].copyMode = 1; } } - return 0; + return global_ctx; } -static void pgsql_commit(void) { +static void pgsql_commit(void * thread_ctxp) { + struct mid_pg_thread_ctx * thread_ctx = thread_ctxp; + struct table_conn * tables_conn = thread_ctx->conn; int i; for (i=0; iflat_node_cache_enabled) writeout_dirty_nodes(thread_ctx->flat_nodes,-1); } static void *pgsql_stop_one(void *arg) { time_t start, end; - struct table_desc *table = arg; + struct table_conn *table = arg; PGconn *sql_conn = table->sql_conn; - fprintf(stderr, "Stopping table: %s\n", table->name); + fprintf(stderr, "Stopping table: %s\n", table->desc->name); pgsql_endCopy(table); time(&start); if (!out_options->droptemp) { - if (build_indexes && table->array_indexes) { - char *buffer = (char *) malloc(strlen(table->array_indexes) + 99); + if (build_indexes && table->desc->array_indexes) { + char *buffer = (char *) malloc(strlen(table->desc->array_indexes) + 99); /* we need to insert before the TABLESPACE setting, if any */ - char *insertpos = strstr(table->array_indexes, "TABLESPACE"); - if (!insertpos) insertpos = strchr(table->array_indexes, ';'); + char *insertpos = strstr(table->desc->array_indexes, "TABLESPACE"); + if (!insertpos) insertpos = strchr(table->desc->array_indexes, ';'); /* automatically insert FASTUPDATE=OFF when creating, indexes for PostgreSQL 8.4 and higher see http://lists.openstreetmap.org/pipermail/dev/2011-January/021704.html */ if (insertpos && PQserverVersion(sql_conn) >= 80400) { char old = *insertpos; - fprintf(stderr, "Building index on table: %s (fastupdate=off)\n", table->name); + fprintf(stderr, "Building index on table: %s (fastupdate=off)\n", table->desc->name); *insertpos = 0; /* temporary null byte for following strcpy operation */ - strcpy(buffer, table->array_indexes); + strcpy(buffer, table->desc->array_indexes); *insertpos = old; /* restore old content */ strcat(buffer, " WITH (FASTUPDATE=OFF)"); strcat(buffer, insertpos); } else { - fprintf(stderr, "Building index on table: %s\n", table->name); - strcpy(buffer, table->array_indexes); + fprintf(stderr, "Building index on table: %s\n", table->desc->name); + strcpy(buffer, table->desc->array_indexes); } pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", buffer); free(buffer); @@ -1752,12 +1823,12 @@ static void *pgsql_stop_one(void *arg) } else { - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "drop table %s", table->name); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "drop table %s", table->desc->name); } PQfinish(sql_conn); table->sql_conn = NULL; time(&end); - fprintf(stderr, "Stopped table: %s in %is\n", table->name, (int)(end - start)); + fprintf(stderr, "Stopped table: %s in %is\n", table->desc->name, (int)(end - start)); return NULL; } @@ -1769,11 +1840,14 @@ static void pgsql_stop(void) #endif free_node_ram_cache(); - if (out_options->flat_node_cache_enabled) shutdown_node_persistent_cache(); + if (out_options->flat_node_cache_enabled) { + shutdown_node_persistent_cache(global_ctx->flat_nodes); + global_ctx->flat_nodes = NULL; + } #ifdef HAVE_PTHREAD for (i=0; iconn[i]); if (ret) { fprintf(stderr, "pthread_create() returned an error (%d)", ret); exit_nicely(); @@ -1788,12 +1862,13 @@ static void pgsql_stop(void) } #else for (i=0; iconn[i]); #endif } struct middle_t mid_pgsql = { .start = pgsql_start, + .connect = pgsql_connect, .stop = pgsql_stop, .cleanup = pgsql_cleanup, .analyze = pgsql_analyze, diff --git a/middle-ram.c b/middle-ram.c index 27d50c232..668de0a20 100644 --- a/middle-ram.c +++ b/middle-ram.c @@ -51,6 +51,10 @@ struct ramRel { * */ +struct thread_ctx { //Dummy structure to pass around + +}; + #define BLOCK_SHIFT 10 #define PER_BLOCK (1 << BLOCK_SHIFT) #define NUM_BLOCKS (1 << (32 - BLOCK_SHIFT)) @@ -81,7 +85,7 @@ static int block2id(int block, int offset) #define UNUSED __attribute__ ((unused)) -static int ram_ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags, int pending) +static int ram_ways_set(void * thread_ctx, osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags, int pending) { int block = id2block(id); int offset = id2offset(id); @@ -124,12 +128,14 @@ static int ram_ways_set(osmid_t id, osmid_t *nds, int nd_count, struct keyval *t return 0; } -static int ram_relations_set(osmid_t id, struct member *members, int member_count, struct keyval *tags) +static int ram_relations_set(void * thread_ctx, osmid_t id, struct member *members, int member_count, struct keyval *tags) { struct keyval *p; struct member *ptr; int block = id2block(id); int offset = id2offset(id); + int i; + if (!rels[block]) { rels[block] = calloc(PER_BLOCK, sizeof(struct ramRel)); if (!rels[block]) { @@ -152,14 +158,23 @@ static int ram_relations_set(osmid_t id, struct member *members, int member_coun cloneList(rels[block][offset].tags, tags); - if (!rels[block][offset].members) + if (!rels[block][offset].members) { + for (i = 0; i < rels[block][offset].member_count; i++) { + if (rels[block][offset].members[i].role) + free ( rels[block][offset].members[i].role ); + } free( rels[block][offset].members ); + } ptr = malloc(sizeof(struct member) * member_count); if (ptr) { memcpy( ptr, members, sizeof(struct member) * member_count ); rels[block][offset].member_count = member_count; rels[block][offset].members = ptr; + for (i = 0; i < member_count; i++) { + rels[block][offset].members[i].role = strdup(members[i].role); + } + } else { fprintf(stderr, "%s malloc failed\n", __FUNCTION__); exit_nicely(); @@ -168,7 +183,7 @@ static int ram_relations_set(osmid_t id, struct member *members, int member_coun return 0; } -static int ram_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_count) +static int ram_nodes_get_list(void * thread_ctx, struct osmNode *nodes, osmid_t *ndids, int nd_count) { int i, count; @@ -185,7 +200,7 @@ static int ram_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_coun static void ram_iterate_relations(int (*callback)(osmid_t id, struct member *members, int member_count, struct keyval *tags, int)) { - int block, offset; + int block, offset, i; fprintf(stderr, "\n"); for(block=NUM_BLOCKS-1; block>=0; block--) { @@ -201,6 +216,9 @@ static void ram_iterate_relations(int (*callback)(osmid_t id, struct member *mem callback(id, rels[block][offset].members, rels[block][offset].member_count, rels[block][offset].tags, 0); } + for (i = 0; i < rels[block][offset].member_count; i++) { + if (rels[block][offset].members[i].role) free( rels[block][offset].members[i].role ); + } free(rels[block][offset].members); rels[block][offset].members = NULL; resetList(rels[block][offset].tags); @@ -233,7 +251,7 @@ static void ram_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, st if (ways[block][offset].pending) { /* First element contains number of nodes */ nodes = malloc( sizeof(struct osmNode) * ways[block][offset].ndids[0]); - ndCount = ram_nodes_get_list(nodes, ways[block][offset].ndids+1, ways[block][offset].ndids[0]); + ndCount = ram_nodes_get_list(NULL, nodes, ways[block][offset].ndids+1, ways[block][offset].ndids[0]); if (nodes) { osmid_t id = block2id(block, offset); @@ -260,7 +278,7 @@ static void ram_iterate_ways(int (*callback)(osmid_t id, struct keyval *tags, st } /* Caller must free nodes_ptr and resetList(tags_ptr) */ -static int ram_ways_get(osmid_t id, struct keyval *tags_ptr, struct osmNode **nodes_ptr, int *count_ptr) +static int ram_ways_get(void * thread_ctx, osmid_t id, struct keyval *tags_ptr, struct osmNode **nodes_ptr, int *count_ptr) { int block = id2block(id), offset = id2offset(id), ndCount = 0; struct osmNode *nodes; @@ -271,7 +289,7 @@ static int ram_ways_get(osmid_t id, struct keyval *tags_ptr, struct osmNode **no if (ways[block][offset].ndids) { /* First element contains number of nodes */ nodes = malloc( sizeof(struct osmNode) * ways[block][offset].ndids[0]); - ndCount = ram_nodes_get_list(nodes, ways[block][offset].ndids+1, ways[block][offset].ndids[0]); + ndCount = ram_nodes_get_list(NULL, nodes, ways[block][offset].ndids+1, ways[block][offset].ndids[0]); if (ndCount) { cloneList( tags_ptr, ways[block][offset].tags ); @@ -284,14 +302,14 @@ static int ram_ways_get(osmid_t id, struct keyval *tags_ptr, struct osmNode **no return 1; } -static int ram_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) { +static int ram_ways_get_list(void * thread_ctx, osmid_t *ids, int way_count, osmid_t **way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr) { int count = 0; int i; *way_ids = malloc( sizeof(osmid_t) * (way_count + 1)); initList(&(tag_ptr[count])); for (i = 0; i < way_count; i++) { - if (ram_ways_get(ids[i], &(tag_ptr[count]), &(node_ptr[count]), &(count_ptr[count])) == 0) { + if (ram_ways_get(NULL, ids[i], &(tag_ptr[count]), &(node_ptr[count]), &(count_ptr[count])) == 0) { (*way_ids)[count] = ids[i]; count++; initList(&(tag_ptr[count])); @@ -301,7 +319,7 @@ static int ram_ways_get_list(osmid_t *ids, int way_count, osmid_t **way_ids, str } /* Marks the way so that iterate ways skips it */ -static int ram_ways_done(osmid_t id) +static int ram_ways_done(void * thread_ctx, osmid_t id) { int block = id2block(id), offset = id2offset(id); @@ -322,8 +340,9 @@ static void ram_end(void) /* No need */ } -static int ram_start(const struct output_options *options) +static void * ram_start(const struct output_options *options) { + struct thread_ctx * ctx = malloc(sizeof(struct thread_ctx)); /* latlong has a range of +-180, mercator +-20000 The fixed poing scaling needs adjusting accordingly to be stored accurately in an int */ @@ -333,7 +352,7 @@ static int ram_start(const struct output_options *options) fprintf( stderr, "Mid: Ram, scale=%d\n", scale ); - return 0; + return ctx; } static void ram_stop(void) @@ -357,14 +376,18 @@ static void ram_stop(void) } } -static void ram_commit(void) { +static void ram_cleanup(void * thread_ctx) { + +} + +static void ram_commit(void * thread_ctx) { } struct middle_t mid_ram = { .start = ram_start, .stop = ram_stop, .end = ram_end, - .cleanup = ram_stop, + .cleanup = ram_cleanup, .analyze = ram_analyze, .commit = ram_commit, .nodes_set = ram_cache_nodes_set, diff --git a/middle.h b/middle.h index c645eda53..b218f79ef 100644 --- a/middle.h +++ b/middle.h @@ -14,32 +14,33 @@ struct member; struct output_options; struct middle_t { - int (*start)(const struct output_options *options); + void * (*start)(const struct output_options *options); + void * (*connect)(const struct output_options *options); void (*stop)(void); - void (*cleanup)(void); + void (*cleanup)(void * thread_ctx); void (*analyze)(void); void (*end)(void); - void (*commit)(void); + void (*commit)(void * thread_ctx); - int (*nodes_set)(osmid_t id, double lat, double lon, struct keyval *tags); - int (*nodes_get_list)(struct osmNode *out, osmid_t *nds, int nd_count); - int (*nodes_delete)(osmid_t id); - int (*node_changed)(osmid_t id); + int (*nodes_set)(void * thread_ctx, osmid_t id, double lat, double lon, struct keyval *tags); + int (*nodes_get_list)(void * thread_ctx, struct osmNode *out, osmid_t *nds, int nd_count); + int (*nodes_delete)(void * thread_ctx, osmid_t id); + int (*node_changed)(void * thread_ctx, osmid_t id); /* int (*nodes_get)(struct osmNode *out, osmid_t id);*/ - int (*ways_set)(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags, int pending); - int (*ways_get)(osmid_t id, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr); - int (*ways_get_list)(osmid_t *ids, int way_count, osmid_t **way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr); + int (*ways_set)(void * thread_ctx, osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags, int pending); + int (*ways_get)(void * thread_ctx, osmid_t id, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr); + int (*ways_get_list)(void * thread_ctx, osmid_t *ids, int way_count, osmid_t **way_ids, struct keyval *tag_ptr, struct osmNode **node_ptr, int *count_ptr); - int (*ways_done)(osmid_t id); - int (*ways_delete)(osmid_t id); - int (*way_changed)(osmid_t id); + int (*ways_done)(void * thread_ctx, osmid_t id); + int (*ways_delete)(void * thread_ctx, osmid_t id); + int (*way_changed)(void * thread_ctx, osmid_t id); - int (*relations_set)(osmid_t id, struct member *members, int member_count, struct keyval *tags); + int (*relations_set)(void * thread_ctx, osmid_t id, struct member *members, int member_count, struct keyval *tags); /* int (*relations_get)(osmid_t id, struct member **members, int *member_count, struct keyval *tags); */ - int (*relations_done)(osmid_t id); - int (*relations_delete)(osmid_t id); - int (*relation_changed)(osmid_t id); + int (*relations_done)(void * thread_ctx, osmid_t id); + int (*relations_delete)(void * thread_ctx, osmid_t id); + int (*relation_changed)(void * thread_ctx, osmid_t id); /* void (*iterate_nodes)(int (*callback)(osmid_t id, struct keyval *tags, double node_lat, double node_lon)); */ void (*iterate_ways)(int (*callback)(osmid_t id, struct keyval *tags, struct osmNode *nodes, int count, int exists)); diff --git a/node-persistent-cache-reader.c b/node-persistent-cache-reader.c index 94b170f1a..17cbd9814 100644 --- a/node-persistent-cache-reader.c +++ b/node-persistent-cache-reader.c @@ -17,13 +17,16 @@ #include "node-ram-cache.h" #include "binarysearcharray.h" +void * thread_ctx; +static struct output_options options; + void exit_nicely() { fprintf(stderr, "Error occurred, cleaning up\n"); exit(1); } -void test_get_node_list(int itterations, int max_size, int process_number) { +void test_get_node_list(void * thread_ctx, int itterations, int max_size, int process_number) { int i, j, node_cnt, node_cnt_total; struct osmNode *nodes; struct timeval start, stop; @@ -43,7 +46,7 @@ void test_get_node_list(int itterations, int max_size, int process_number) { osmids[j] = random() % (1 << 31); } gettimeofday(&start, NULL); - persistent_cache_nodes_get_list(nodes,osmids,node_cnt); + persistent_cache_nodes_get_list(thread_ctx, nodes,osmids,node_cnt); gettimeofday(&stop, NULL); double duration = ((stop.tv_sec - start.tv_sec)*1000000.0 + (stop.tv_usec - start.tv_usec))/1000000.0; printf("Process %i: Got nodes in %f at a rate of %f/s\n", process_number, duration, node_cnt / duration); @@ -55,9 +58,16 @@ void test_get_node_list(int itterations, int max_size, int process_number) { printf("Process %i: Got a total of nodes in %f at a rate of %f/s\n", process_number, duration, node_cnt_total / duration); } +static void * node_persistent_worker_thread(void * pointer) { + void * thread_ctx; + int * tid = pointer; + thread_ctx = init_node_persistent_cache(&options, 1); + test_get_node_list(thread_ctx, 10,200,*tid); +} + int main(int argc, char *argv[]) { int i,p; - struct output_options options; + struct osmNode node; struct osmNode *nodes; struct timeval start; @@ -71,14 +81,14 @@ int main(int argc, char *argv[]) { if (argc > 3) { - init_node_persistent_cache(&options, 1); + thread_ctx = init_node_persistent_cache(&options, 1); node_cnt = argc - 2; nodes = malloc(sizeof(struct osmNode) * node_cnt); osmids = malloc(sizeof(osmid_t) * node_cnt); for (i = 0; i < node_cnt; i++) { osmids[i] = atoi(argv[2 + i]); } - persistent_cache_nodes_get_list(nodes,osmids,node_cnt); + persistent_cache_nodes_get_list(thread_ctx, nodes,osmids,node_cnt); for (i = 0; i < node_cnt; i++) { printf("lat: %f / lon: %f\n", nodes[i].lat, nodes[i].lon); } @@ -89,9 +99,9 @@ int main(int argc, char *argv[]) { setstate(state); printf("Testing mode\n"); - init_node_persistent_cache(&options, 1); - test_get_node_list(10, 200, 0); - shutdown_node_persistent_cache(); + thread_ctx = init_node_persistent_cache(&options, 1); + //test_get_node_list(thread_ctx, 10, 200, 0);// TODO: uncomment + shutdown_node_persistent_cache(thread_ctx); #ifdef HAVE_FORK printf("Testing using multiple processes\n"); int noProcs = 4; @@ -109,11 +119,11 @@ int main(int argc, char *argv[]) { gettimeofday(&start, NULL); initstate(start.tv_usec, state, 8); setstate(state); - init_node_persistent_cache(&options, 1); - test_get_node_list(10,200,p); + thread_ctx = init_node_persistent_cache(&options, 1); + //test_get_node_list(thread_ctx, 10,200,p); //TODO: uncomment if (pid == 0) { - shutdown_node_persistent_cache(); + shutdown_node_persistent_cache(thread_ctx); fprintf(stderr,"Exiting process %i\n", p); exit(0); } else { @@ -121,11 +131,24 @@ int main(int argc, char *argv[]) { } free(state); fprintf(stderr, "\nAll child processes exited\n"); +#endif +#ifdef HAVE_PTHREAD + printf("Testing using multi-threading\n"); + noProcs = 20; + pthread_t * worker_threads = malloc(sizeof(pthread_t) * noProcs); + for (p = 0; p < noProcs; p++) { + int * tid = malloc(sizeof(int)); + *tid = p; + pthread_create(&(worker_threads[p]),NULL,node_persistent_worker_thread, tid); + } + for (p = 0; p < noProcs; p++) { + pthread_join(worker_threads[p], NULL); + } #endif } else { - init_node_persistent_cache(&options, 1); + thread_ctx = init_node_persistent_cache(&options, 1); if (strstr(argv[2],",") == NULL) { - persistent_cache_nodes_get(&node, atoi(argv[2])); + persistent_cache_nodes_get(thread_ctx, &node, atoi(argv[2])); printf("lat: %f / lon: %f\n", node.lat, node.lon); } else { char * node_list = malloc(sizeof(char) * (strlen(argv[2]) + 1)); @@ -142,7 +165,7 @@ int main(int argc, char *argv[]) { char * tmp = strtok(NULL,","); osmids[i] = atoi(tmp); } - persistent_cache_nodes_get_list(nodes,osmids,node_cnt); + persistent_cache_nodes_get_list(thread_ctx, nodes,osmids,node_cnt); for (i = 0; i < node_cnt; i++) { printf("lat: %f / lon: %f\n", nodes[i].lat, nodes[i].lon); } @@ -150,6 +173,6 @@ int main(int argc, char *argv[]) { } - shutdown_node_persistent_cache(); + shutdown_node_persistent_cache(thread_ctx); return 0; } diff --git a/node-persistent-cache.c b/node-persistent-cache.c index bd1047a6a..a8c8ae8f9 100644 --- a/node-persistent-cache.c +++ b/node-persistent-cache.c @@ -13,6 +13,11 @@ #include #include +#ifdef HAVE_PTHREAD +#include +#endif + + #include "osmtypes.h" #include "output.h" #include "node-persistent-cache.h" @@ -31,27 +36,28 @@ #endif #endif -static int node_cache_fd; static const char * node_cache_fname; static int append_mode; +#ifdef HAVE_PTHREAD +pthread_mutex_t lock_node_persistent_cache = PTHREAD_MUTEX_INITIALIZER; +#endif struct persistentCacheHeader cacheHeader; static struct ramNodeBlock writeNodeBlock; /* larger node block for more efficient initial sequential writing of node cache */ -static struct ramNodeBlock * readNodeBlockCache; -static struct binary_search_array * readNodeBlockCacheIdx; static int scale; static int cache_already_written = 0; -static void writeout_dirty_nodes(osmid_t id) +void writeout_dirty_nodes(void * ctx_p, osmid_t id) { + struct node_persistent_thread_ctx * thread_ctx = ctx_p; int i; if (writeNodeBlock.dirty > 0) { - if (lseek64(node_cache_fd, + if (lseek64(thread_ctx->node_cache_fd, (writeNodeBlock.block_offset << WRITE_NODE_BLOCK_SHIFT) * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), SEEK_SET) < 0) { @@ -60,7 +66,7 @@ static void writeout_dirty_nodes(osmid_t id) exit_nicely(); }; - if (write(node_cache_fd, writeNodeBlock.nodes, + if (write(thread_ctx->node_cache_fd, writeNodeBlock.nodes, WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode)) < WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode)) { @@ -72,12 +78,12 @@ static void writeout_dirty_nodes(osmid_t id) << WRITE_NODE_BLOCK_SHIFT) - 1; writeNodeBlock.used = 0; writeNodeBlock.dirty = 0; - if (lseek64(node_cache_fd, 0, SEEK_SET) < 0) { + if (lseek64(thread_ctx->node_cache_fd, 0, SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; - if (write(node_cache_fd, &cacheHeader, + if (write(thread_ctx->node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) != sizeof(struct persistentCacheHeader)) { @@ -85,19 +91,20 @@ static void writeout_dirty_nodes(osmid_t id) strerror(errno)); exit_nicely(); } - if (fsync(node_cache_fd) < 0) { - fprintf(stderr, "Info: Node cache could not be guaranteeded to be made durable. fsync failed: %s\n", + if (fsync(thread_ctx->node_cache_fd) < 0) { + fprintf(stderr, "Info: Node cache could not be guaranteed to be made durable. fsync failed: %s\n", strerror(errno)); }; } - if (id < 0) + + if (id < 0) //Indicate that we want to write out any dirty readNode cache as well, used in append mode. { for (i = 0; i < READ_NODE_CACHE_SIZE; i++) { - if (readNodeBlockCache[i].dirty) + if (thread_ctx->readNodeBlockCache[i].dirty) { - if (lseek64(node_cache_fd, - (readNodeBlockCache[i].block_offset + if (lseek64(thread_ctx->node_cache_fd, + (thread_ctx->readNodeBlockCache[i].block_offset << READ_NODE_BLOCK_SHIFT) * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), @@ -106,7 +113,7 @@ static void writeout_dirty_nodes(osmid_t id) strerror(errno)); exit_nicely(); }; - if (write(node_cache_fd, readNodeBlockCache[i].nodes, + if (write(thread_ctx->node_cache_fd, thread_ctx->readNodeBlockCache[i].nodes, READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) < READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) { @@ -115,7 +122,7 @@ static void writeout_dirty_nodes(osmid_t id) exit_nicely(); } } - readNodeBlockCache[i].dirty = 0; + thread_ctx->readNodeBlockCache[i].dirty = 0; } } @@ -139,7 +146,7 @@ static void ramNodes_clear(struct ramNode * nodes, int size) /** * Find the cache block with the lowest usage count for replacement */ -static int persistent_cache_replace_block() +static int persistent_cache_replace_block(struct node_persistent_thread_ctx * ctx) { int min_used = INT_MAX; int block_id = -1; @@ -147,9 +154,9 @@ static int persistent_cache_replace_block() for (i = 0; i < READ_NODE_CACHE_SIZE; i++) { - if (readNodeBlockCache[i].used < min_used) + if (ctx->readNodeBlockCache[i].used < min_used) { - min_used = readNodeBlockCache[i].used; + min_used = ctx->readNodeBlockCache[i].used; block_id = i; } } @@ -157,9 +164,9 @@ static int persistent_cache_replace_block() { for (i = 0; i < READ_NODE_CACHE_SIZE; i++) { - if (readNodeBlockCache[i].used > 1) + if (ctx->readNodeBlockCache[i].used > 1) { - readNodeBlockCache[i].used--; + ctx->readNodeBlockCache[i].used--; } } } @@ -169,16 +176,16 @@ static int persistent_cache_replace_block() /** * Find cache block number by block_offset */ -static int persistent_cache_find_block(osmid_t block_offset) +static int persistent_cache_find_block(struct node_persistent_thread_ctx * ctx, osmid_t block_offset) { - int idx = binary_search_get(readNodeBlockCacheIdx, block_offset); + int idx = binary_search_get(ctx->readNodeBlockCacheIdx, block_offset); return idx; } /** * Initialise the persistent cache with NaN values to identify which IDs are valid or not */ -static void persistent_cache_expand_cache(osmid_t block_offset) +static void persistent_cache_expand_cache(struct node_persistent_thread_ctx * ctx, osmid_t block_offset) { osmid_t i; struct ramNode * dummyNodes = malloc( @@ -189,7 +196,7 @@ static void persistent_cache_expand_cache(osmid_t block_offset) } ramNodes_clear(dummyNodes, READ_NODE_BLOCK_SIZE); /* Need to expand the persistent node cache */ - if (lseek64(node_cache_fd, + if (lseek64(ctx->node_cache_fd, cacheHeader.max_initialised_id * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", @@ -199,7 +206,7 @@ static void persistent_cache_expand_cache(osmid_t block_offset) for (i = cacheHeader.max_initialised_id >> READ_NODE_BLOCK_SHIFT; i <= block_offset; i++) { - if (write(node_cache_fd, dummyNodes, + if (write(ctx->node_cache_fd, dummyNodes, READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) < READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) { @@ -210,12 +217,12 @@ static void persistent_cache_expand_cache(osmid_t block_offset) } cacheHeader.max_initialised_id = ((block_offset + 1) << READ_NODE_BLOCK_SHIFT) - 1; - if (lseek64(node_cache_fd, 0, SEEK_SET) < 0) { + if (lseek64(ctx->node_cache_fd, 0, SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; - if (write(node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) + if (write(ctx->node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) != sizeof(struct persistentCacheHeader)) { fprintf(stderr, "Failed to update persistent cache header: %s\n", @@ -223,27 +230,34 @@ static void persistent_cache_expand_cache(osmid_t block_offset) exit_nicely(); } free(dummyNodes); - fsync(node_cache_fd); + fsync(ctx->node_cache_fd); } -static void persistent_cache_nodes_prefetch_async(osmid_t id) +static void persistent_cache_nodes_prefetch_async(struct node_persistent_thread_ctx * ctx, osmid_t id) { #ifdef HAVE_POSIX_FADVISE osmid_t block_offset = id >> READ_NODE_BLOCK_SHIFT; - osmid_t block_id = persistent_cache_find_block(block_offset); + osmid_t block_id = persistent_cache_find_block(ctx, block_offset); if (block_id < 0) { /* The needed block isn't in cache already, so initiate loading */ - writeout_dirty_nodes(id); + writeout_dirty_nodes(ctx, id); /* Make sure the node cache is correctly initialised for the block that will be read */ +#ifdef HAVE_PTHREAD + // As this is a write operation, we need to make sure we synchronize this between all threads + pthread_mutex_lock(&lock_node_persistent_cache); +#endif if (cacheHeader.max_initialised_id < ((block_offset + 1) << READ_NODE_BLOCK_SHIFT)) - persistent_cache_expand_cache(block_offset); + persistent_cache_expand_cache(ctx,block_offset); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&lock_node_persistent_cache); +#endif - if (posix_fadvise(node_cache_fd, (block_offset << READ_NODE_BLOCK_SHIFT) * sizeof(struct ramNode) + if (posix_fadvise(ctx->node_cache_fd, (block_offset << READ_NODE_BLOCK_SHIFT) * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), READ_NODE_BLOCK_SIZE * sizeof(struct ramNode), POSIX_FADV_WILLNEED | POSIX_FADV_RANDOM) != 0) { fprintf(stderr, "Info: async prefetch of node cache failed. This might reduce performance\n"); @@ -256,22 +270,23 @@ static void persistent_cache_nodes_prefetch_async(osmid_t id) /** * Load block offset in a synchronous way. */ -static int persistent_cache_load_block(osmid_t block_offset) +static int persistent_cache_load_block(struct node_persistent_thread_ctx * ctx, osmid_t block_offset) { - int block_id = persistent_cache_replace_block(); + int block_id = persistent_cache_replace_block(ctx); - if (readNodeBlockCache[block_id].dirty) + /* This should only happen if we are in single threaded mode. Otherwise it is likely to lead to thread inconsistencies */ + if (ctx->readNodeBlockCache[block_id].dirty) { - if (lseek64(node_cache_fd, - (readNodeBlockCache[block_id].block_offset + if (lseek64(ctx->node_cache_fd, + (ctx->readNodeBlockCache[block_id].block_offset << READ_NODE_BLOCK_SHIFT) * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; - if (write(node_cache_fd, readNodeBlockCache[block_id].nodes, + if (write(ctx->node_cache_fd, ctx->readNodeBlockCache[block_id].nodes, READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) < READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) { @@ -279,31 +294,39 @@ static int persistent_cache_load_block(osmid_t block_offset) strerror(errno)); exit_nicely(); } - readNodeBlockCache[block_id].dirty = 0; + ctx->readNodeBlockCache[block_id].dirty = 0; } - binary_search_remove(readNodeBlockCacheIdx, - readNodeBlockCache[block_id].block_offset); - ramNodes_clear(readNodeBlockCache[block_id].nodes, READ_NODE_BLOCK_SIZE); - readNodeBlockCache[block_id].block_offset = block_offset; - readNodeBlockCache[block_id].used = READ_NODE_CACHE_SIZE; + binary_search_remove(ctx->readNodeBlockCacheIdx, + ctx->readNodeBlockCache[block_id].block_offset); + ramNodes_clear(ctx->readNodeBlockCache[block_id].nodes, READ_NODE_BLOCK_SIZE); + ctx->readNodeBlockCache[block_id].block_offset = block_offset; + ctx->readNodeBlockCache[block_id].used = READ_NODE_CACHE_SIZE; // As we are just loading this, make sure it has a high priority in the LRU /* Make sure the node cache is correctly initialised for the block that will be read */ +#ifdef HAVE_PTHREAD + // As this is a write operation, we need to make sure we synchronize this between all threads + pthread_mutex_lock(&lock_node_persistent_cache); +#endif if (cacheHeader.max_initialised_id < ((block_offset + 1) << READ_NODE_BLOCK_SHIFT)) { - persistent_cache_expand_cache(block_offset); + persistent_cache_expand_cache(ctx, block_offset); } +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&lock_node_persistent_cache); +#endif + /* Read the block into cache */ - if (lseek64(node_cache_fd, + if (lseek64(ctx->node_cache_fd, (block_offset << READ_NODE_BLOCK_SHIFT) * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; - if (read(node_cache_fd, readNodeBlockCache[block_id].nodes, + if (read(ctx->node_cache_fd, ctx->readNodeBlockCache[block_id].nodes, READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) != READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)) { @@ -311,15 +334,15 @@ static int persistent_cache_load_block(osmid_t block_offset) strerror(errno)); exit(1); } - binary_search_add(readNodeBlockCacheIdx, - readNodeBlockCache[block_id].block_offset, block_id); + binary_search_add(ctx->readNodeBlockCacheIdx, + ctx->readNodeBlockCache[block_id].block_offset, block_id); return block_id; } -static void persisten_cache_nodes_set_create_writeout_block() +static void persisten_cache_nodes_set_create_writeout_block(struct node_persistent_thread_ctx * ctx) { - if (write(node_cache_fd, writeNodeBlock.nodes, + if (write(ctx->node_cache_fd, writeNodeBlock.nodes, WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode)) < WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode)) { @@ -338,7 +361,7 @@ static void persisten_cache_nodes_set_create_writeout_block() * node cache file in buffer cache therefore duplicates the data wasting 16GB of ram. * Therefore tell the OS not to cache the node-persistent-cache during initial import. * */ - if (sync_file_range(node_cache_fd, writeNodeBlock.block_offset*WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode) + + if (sync_file_range(ctx->node_cache_fd, writeNodeBlock.block_offset*WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode), SYNC_FILE_RANGE_WRITE) < 0) { fprintf(stderr, "Info: Sync_file_range writeout has an issue. This shouldn't be anything to worry about.: %s\n", @@ -346,7 +369,7 @@ static void persisten_cache_nodes_set_create_writeout_block() }; if (writeNodeBlock.block_offset > 16) { - if(sync_file_range(node_cache_fd, (writeNodeBlock.block_offset - 16)*WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode) + + if(sync_file_range(ctx->node_cache_fd, (writeNodeBlock.block_offset - 16)*WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode), SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER) < 0) { fprintf(stderr, "Info: Sync_file_range block has an issue. This shouldn't be anything to worry about.: %s\n", @@ -354,7 +377,7 @@ static void persisten_cache_nodes_set_create_writeout_block() } #ifdef HAVE_POSIX_FADVISE - if (posix_fadvise(node_cache_fd, (writeNodeBlock.block_offset - 16)*WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode) + + if (posix_fadvise(ctx->node_cache_fd, (writeNodeBlock.block_offset - 16)*WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode) + sizeof(struct persistentCacheHeader), WRITE_NODE_BLOCK_SIZE * sizeof(struct ramNode), POSIX_FADV_DONTNEED) !=0 ) { fprintf(stderr, "Info: Posix_fadvise failed. This shouldn't be anything to worry about.: %s\n", strerror(errno)); @@ -364,7 +387,7 @@ static void persisten_cache_nodes_set_create_writeout_block() #endif } -static int persistent_cache_nodes_set_create(osmid_t id, double lat, double lon) +static int persistent_cache_nodes_set_create(struct node_persistent_thread_ctx * ctx, osmid_t id, double lat, double lon) { osmid_t block_offset = id >> WRITE_NODE_BLOCK_SHIFT; int i; @@ -376,7 +399,7 @@ static int persistent_cache_nodes_set_create(osmid_t id, double lat, double lon) { if (writeNodeBlock.dirty) { - persisten_cache_nodes_set_create_writeout_block(); + persisten_cache_nodes_set_create_writeout_block(ctx); writeNodeBlock.used = 0; writeNodeBlock.dirty = 0; /* After writing out the node block, the file pointer is at the next block level */ @@ -396,7 +419,7 @@ static int persistent_cache_nodes_set_create(osmid_t id, double lat, double lon) for (i = writeNodeBlock.block_offset; i < block_offset; i++) { ramNodes_clear(writeNodeBlock.nodes, WRITE_NODE_BLOCK_SIZE); - persisten_cache_nodes_set_create_writeout_block(); + persisten_cache_nodes_set_create_writeout_block(ctx); } ramNodes_clear(writeNodeBlock.nodes, WRITE_NODE_BLOCK_SIZE); @@ -416,65 +439,67 @@ static int persistent_cache_nodes_set_create(osmid_t id, double lat, double lon) return 0; } -static int persistent_cache_nodes_set_append(osmid_t id, double lat, double lon) +static int persistent_cache_nodes_set_append(struct node_persistent_thread_ctx * ctx, osmid_t id, double lat, double lon) { osmid_t block_offset = id >> READ_NODE_BLOCK_SHIFT; - int block_id = persistent_cache_find_block(block_offset); + int block_id = persistent_cache_find_block(ctx, block_offset); if (block_id < 0) - block_id = persistent_cache_load_block(block_offset); + block_id = persistent_cache_load_block(ctx, block_offset); #ifdef FIXED_POINT if (isnan(lat) && isnan(lon)) { - readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat = + ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat = INT_MIN; - readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon = + ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon = INT_MIN; } else { - readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat = + ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat = DOUBLE_TO_FIX(lat); - readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon = + ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon = DOUBLE_TO_FIX(lon); } #else readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat = lat; readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon = lon; #endif - readNodeBlockCache[block_id].used++; - readNodeBlockCache[block_id].dirty = 1; + ctx->readNodeBlockCache[block_id].used++; + ctx->readNodeBlockCache[block_id].dirty = 1; return 1; } -int persistent_cache_nodes_set(osmid_t id, double lat, double lon) +int persistent_cache_nodes_set(void * ctx_p, osmid_t id, double lat, double lon) { + struct node_persistent_thread_ctx * ctx = ctx_p; return append_mode ? - persistent_cache_nodes_set_append(id, lat, lon) : - persistent_cache_nodes_set_create(id, lat, lon); + persistent_cache_nodes_set_append(ctx, id, lat, lon) : + persistent_cache_nodes_set_create(ctx, id, lat, lon); } -int persistent_cache_nodes_get(struct osmNode *out, osmid_t id) +int persistent_cache_nodes_get(void * ctx_p, struct osmNode *out, osmid_t id) { + struct node_persistent_thread_ctx * ctx = ctx_p; osmid_t block_offset = id >> READ_NODE_BLOCK_SHIFT; - osmid_t block_id = persistent_cache_find_block(block_offset); + osmid_t block_id = persistent_cache_find_block(ctx, block_offset); if (block_id < 0) { - writeout_dirty_nodes(id); - block_id = persistent_cache_load_block(block_offset); + writeout_dirty_nodes(ctx, id); + block_id = persistent_cache_load_block(ctx, block_offset); } - readNodeBlockCache[block_id].used++; + ctx->readNodeBlockCache[block_id].used++; #ifdef FIXED_POINT - if ((readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat + if ((ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat == INT_MIN) - && (readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon + && (ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon == INT_MIN)) { return 1; @@ -482,9 +507,9 @@ int persistent_cache_nodes_get(struct osmNode *out, osmid_t id) else { out->lat = - FIX_TO_DOUBLE(readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat); + FIX_TO_DOUBLE(ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lat); out->lon = - FIX_TO_DOUBLE(readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon); + FIX_TO_DOUBLE(ctx->readNodeBlockCache[block_id].nodes[id & READ_NODE_BLOCK_MASK].lon); return 0; } #else @@ -504,9 +529,10 @@ int persistent_cache_nodes_get(struct osmNode *out, osmid_t id) return 0; } -int persistent_cache_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, +int persistent_cache_nodes_get_list(void * ctx_p, struct osmNode *nodes, osmid_t *ndids, int nd_count) { + struct node_persistent_thread_ctx * ctx = ctx_p; int count = 0; int i; for (i = 0; i < nd_count; i++) @@ -530,12 +556,12 @@ int persistent_cache_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, /* In order to have a higher OS level I/O queue depth issue posix_fadvise(WILLNEED) requests for all I/O */ if (isnan(nodes[i].lat) && isnan(nodes[i].lon)) - persistent_cache_nodes_prefetch_async(ndids[i]); + persistent_cache_nodes_prefetch_async(ctx, ndids[i]); } for (i = 0; i < nd_count; i++) { if ((isnan(nodes[i].lat) && isnan(nodes[i].lon)) - && (persistent_cache_nodes_get(&(nodes[i]), ndids[i]) == 0)) + && (persistent_cache_nodes_get(ctx, &(nodes[i]), ndids[i]) == 0)) count++; } @@ -561,8 +587,8 @@ int persistent_cache_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, return count; } -void init_node_persistent_cache(const struct output_options *options, int append) -{ +void * init_node_persistent_cache(const struct output_options *options, int append) { + struct node_persistent_thread_ctx * ctx = malloc(sizeof(struct node_persistent_thread_ctx)); int i, err; scale = options->scale; append_mode = append; @@ -570,13 +596,22 @@ void init_node_persistent_cache(const struct output_options *options, int append fprintf(stderr, "Mid: loading persistent node cache from %s\n", node_cache_fname); - readNodeBlockCacheIdx = init_search_array(READ_NODE_CACHE_SIZE); + /* To be thread safe in reading from the node_persistent cache + * we create a per thread context for all read operations. This contains + * the file descriptor to ensure we can seek in the node file independently + * in each thread. We also keep separate read caches per thread to not have to + * synchronize between threads. As we don't synchronize between threads, we + * need to make sure that all write operations have happened before + * the threading stage. + * + */ + ctx->readNodeBlockCacheIdx = init_search_array(READ_NODE_CACHE_SIZE); /* Setup the file for the node position cache */ if (append_mode) { - node_cache_fd = open(node_cache_fname, O_RDWR, S_IRUSR | S_IWUSR); - if (node_cache_fd < 0) + ctx->node_cache_fd = open(node_cache_fname, O_RDWR, S_IRUSR | S_IWUSR); + if (ctx->node_cache_fd < 0) { fprintf(stderr, "Failed to open node cache file: %s\n", strerror(errno)); @@ -585,32 +620,34 @@ void init_node_persistent_cache(const struct output_options *options, int append } else { +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&lock_node_persistent_cache); +#endif if (cache_already_written) { - node_cache_fd = open(node_cache_fname, O_RDWR, S_IRUSR | S_IWUSR); + ctx->node_cache_fd = open(node_cache_fname, O_RDWR, S_IRUSR | S_IWUSR); } else { - node_cache_fd = open(node_cache_fname, O_RDWR | O_CREAT | O_TRUNC, + ctx->node_cache_fd = open(node_cache_fname, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); } - if (node_cache_fd < 0) + if (ctx->node_cache_fd < 0) { fprintf(stderr, "Failed to create node cache file: %s\n", strerror(errno)); exit_nicely(); } - if (lseek64(node_cache_fd, 0, SEEK_SET) < 0) { + if (lseek64(ctx->node_cache_fd, 0, SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; if (cache_already_written == 0) { - #ifdef HAVE_POSIX_FALLOCATE - if ((err = posix_fallocate(node_cache_fd, 0, + if ((err = posix_fallocate(ctx->node_cache_fd, 0, sizeof(struct ramNode) * MAXIMUM_INITIAL_ID)) != 0) { if (err == ENOSPC) { @@ -620,8 +657,7 @@ void init_node_persistent_cache(const struct output_options *options, int append } else { fprintf(stderr, "Failed to allocate space for node cache file: Internal error %i\n", err); } - - close(node_cache_fd); + close(ctx->node_cache_fd); exit_nicely(); } fprintf(stderr, "Allocated space for persistent node cache file\n"); @@ -639,12 +675,12 @@ void init_node_persistent_cache(const struct output_options *options, int append cacheHeader.format_version = PERSISTENT_CACHE_FORMAT_VERSION; cacheHeader.id_size = sizeof(osmid_t); cacheHeader.max_initialised_id = 0; - if (lseek64(node_cache_fd, 0, SEEK_SET) < 0) { + if (lseek64(ctx->node_cache_fd, 0, SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; - if (write(node_cache_fd, &cacheHeader, + if (write(ctx->node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) != sizeof(struct persistentCacheHeader)) { @@ -653,14 +689,18 @@ void init_node_persistent_cache(const struct output_options *options, int append exit_nicely(); } } +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&lock_node_persistent_cache); +#endif + } - if (lseek64(node_cache_fd, 0, SEEK_SET) < 0) { + if (lseek64(ctx->node_cache_fd, 0, SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; - if (read(node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) + if (read(ctx->node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) != sizeof(struct persistentCacheHeader)) { fprintf(stderr, "Failed to read persistent cache header: %s\n", @@ -681,37 +721,42 @@ void init_node_persistent_cache(const struct output_options *options, int append fprintf(stderr,"Maximum node in persistent node cache: %" PRIdOSMID "\n", cacheHeader.max_initialised_id); - readNodeBlockCache = malloc( + ctx->readNodeBlockCache = malloc( READ_NODE_CACHE_SIZE * sizeof(struct ramNodeBlock)); - if (!readNodeBlockCache) { + if (!ctx->readNodeBlockCache) { fprintf(stderr, "Out of memory: Failed to allocate node read cache\n"); exit_nicely(); } for (i = 0; i < READ_NODE_CACHE_SIZE; i++) { - readNodeBlockCache[i].nodes = malloc( + ctx->readNodeBlockCache[i].nodes = malloc( READ_NODE_BLOCK_SIZE * sizeof(struct ramNode)); - if (!readNodeBlockCache[i].nodes) { + if (!ctx->readNodeBlockCache[i].nodes) { fprintf(stderr, "Out of memory: Failed to allocate node read cache\n"); exit_nicely(); } - readNodeBlockCache[i].block_offset = -1; - readNodeBlockCache[i].used = 0; - readNodeBlockCache[i].dirty = 0; + ctx->readNodeBlockCache[i].block_offset = -1; + ctx->readNodeBlockCache[i].used = 0; + ctx->readNodeBlockCache[i].dirty = 0; } + return ctx; } -void shutdown_node_persistent_cache() +void shutdown_node_persistent_cache(void * ctx_p) { + struct node_persistent_thread_ctx * ctx = ctx_p; int i; - writeout_dirty_nodes(-1); + writeout_dirty_nodes(ctx, -1); - if (lseek64(node_cache_fd, 0, SEEK_SET) < 0) { +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&lock_node_persistent_cache); +#endif + if (lseek64(ctx->node_cache_fd, 0, SEEK_SET) < 0) { fprintf(stderr, "Failed to seek to correct position in node cache: %s\n", strerror(errno)); exit_nicely(); }; - if (write(node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) + if (write(ctx->node_cache_fd, &cacheHeader, sizeof(struct persistentCacheHeader)) != sizeof(struct persistentCacheHeader)) { fprintf(stderr, "Failed to update persistent cache header: %s\n", @@ -720,9 +765,12 @@ void shutdown_node_persistent_cache() } fprintf(stderr,"Maximum node in persistent node cache: %" PRIdOSMID "\n", cacheHeader.max_initialised_id); - fsync(node_cache_fd); + fsync(ctx->node_cache_fd); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&lock_node_persistent_cache); +#endif - if (close(node_cache_fd) != 0) + if (close(ctx->node_cache_fd) != 0) { fprintf(stderr, "Failed to close node cache file: %s\n", strerror(errno)); @@ -730,9 +778,10 @@ void shutdown_node_persistent_cache() for (i = 0; i < READ_NODE_CACHE_SIZE; i++) { - free(readNodeBlockCache[i].nodes); + free(ctx->readNodeBlockCache[i].nodes); } - shutdown_search_array(&readNodeBlockCacheIdx); - free(readNodeBlockCache); - readNodeBlockCache = NULL; + shutdown_search_array(&ctx->readNodeBlockCacheIdx); + free(ctx->readNodeBlockCache); + ctx->readNodeBlockCache = NULL; + free(ctx); } diff --git a/node-persistent-cache.h b/node-persistent-cache.h index babf9a54e..b5d53896f 100644 --- a/node-persistent-cache.h +++ b/node-persistent-cache.h @@ -17,8 +17,15 @@ struct persistentCacheHeader { osmid_t max_initialised_id; }; -int persistent_cache_nodes_set(osmid_t id, double lat, double lon); -int persistent_cache_nodes_get(struct osmNode *out, osmid_t id); -int persistent_cache_nodes_get_list(struct osmNode *nodes, osmid_t *ndids, int nd_count); -void init_node_persistent_cache(const struct output_options *options, const int append); -void shutdown_node_persistent_cache(); +struct node_persistent_thread_ctx { + int node_cache_fd; + struct ramNodeBlock * readNodeBlockCache; + struct binary_search_array * readNodeBlockCacheIdx; +}; + +int persistent_cache_nodes_set(void * ctx_p, osmid_t id, double lat, double lon); +int persistent_cache_nodes_get(void * ctx_p, struct osmNode *out, osmid_t id); +int persistent_cache_nodes_get_list(void * ctx_p, struct osmNode *nodes, osmid_t *ndids, int nd_count); +void writeout_dirty_nodes(void * ctx_p, osmid_t id); +void * init_node_persistent_cache(const struct output_options *options, const int append); +void shutdown_node_persistent_cache(void * ctx_p); diff --git a/node-ram-cache.c b/node-ram-cache.c index 496fc1779..988b718b8 100644 --- a/node-ram-cache.c +++ b/node-ram-cache.c @@ -442,7 +442,7 @@ void free_node_ram_cache() { } } -int ram_cache_nodes_set(osmid_t id, double lat, double lon, struct keyval *tags UNUSED) { +int ram_cache_nodes_set(void * thread_ctx, osmid_t id, double lat, double lon, struct keyval *tags UNUSED) { totalNodes++; /* if ALLOC_DENSE and ALLOC_SPARSE are set, send it through * ram_nodes_set_dense. If a block is non dense, it will automatically diff --git a/node-ram-cache.h b/node-ram-cache.h index 14e76928f..2daa4a58e 100644 --- a/node-ram-cache.h +++ b/node-ram-cache.h @@ -47,7 +47,7 @@ struct ramNodeBlock { void init_node_ram_cache(int strategy, int cacheSizeMB, int fixpointscale); void free_node_ram_cache(); -int ram_cache_nodes_set(osmid_t id, double lat, double lon, struct keyval *tags UNUSED); +int ram_cache_nodes_set(void * thread_ctx, osmid_t id, double lat, double lon, struct keyval *tags UNUSED); int ram_cache_nodes_get(struct osmNode *out, osmid_t id); #endif diff --git a/osm2pgsql.c b/osm2pgsql.c index 92a05718f..1ea989845 100644 --- a/osm2pgsql.c +++ b/osm2pgsql.c @@ -102,7 +102,11 @@ static int parse_bbox(struct osmdata_t *osmdata) void exit_nicely() { fprintf(stderr, "Error occurred, cleaning up\n"); - osmdata.out->cleanup(); + void * buffer[255]; + const int calls = backtrace(buffer, + sizeof(buffer) / sizeof(void *)); + backtrace_symbols_fd(buffer, calls, 1); + osmdata.out->cleanup(NULL); exit(1); } @@ -398,7 +402,9 @@ int main(int argc, char *argv[]) int alloc_chunkwise = ALLOC_SPARSE; #endif int num_procs = 1; + int num_threads = 1; int droptemp = 0; + int cluster = 1; int unlogged = 0; int excludepoly = 0; time_t start, end; @@ -484,6 +490,7 @@ int main(int argc, char *argv[]) {"flat-nodes",1,0,209}, {"exclude-invalid-polygon",0,0,210}, {"tag-transform-script",1,0,212}, + {"cluster", 1, 0, 213}, {0, 0, 0, 0} }; @@ -525,9 +532,11 @@ int main(int argc, char *argv[]) case 'o': expire_tiles_filename=optarg; break; case 'O': output_backend = optarg; break; case 'x': osmdata.extra_attributes=1; break; - case 'k': enable_hstore=HSTORE_NORM; break; + case 'k': if (enable_hstore != HSTORE_NONE) { fprintf(stderr, "ERROR: You can not specify both --hstore (-k) and --hstore-all (-j)\n"); exit (EXIT_FAILURE); } + enable_hstore=HSTORE_NORM; break; case 208: hstore_match_only = 1; break; - case 'j': enable_hstore=HSTORE_ALL; break; + case 'j': if (enable_hstore != HSTORE_NONE) { fprintf(stderr, "ERROR: You can not specify both --hstore (-k) and --hstore-all (-j)\n"); exit (EXIT_FAILURE); } + enable_hstore=HSTORE_ALL; break; case 'z': n_hstore_columns++; hstore_columns = (const char**)realloc(hstore_columns, sizeof(char *) * n_hstore_columns); @@ -551,6 +560,12 @@ int main(int argc, char *argv[]) case 205: #ifdef HAVE_FORK num_procs = atoi(optarg); + num_threads = num_procs; + /* The worker threads don't scale very well, and it appears though beyond + * about 6 threads, there is no further speedup, or even a slowdown with more threads + * "going over pending ways however does benefit from more processes, so limit the + * number of threads to 6 until we can figure out what the barrier for scaling is */ + if (num_threads > 6) num_threads = 6; #else fprintf(stderr, "WARNING: osm2pgsql was compiled without fork, only using one process!\n"); #endif @@ -564,6 +579,12 @@ int main(int argc, char *argv[]) case 210: excludepoly = 1; exclude_broken_polygon(); break; case 211: enable_hstore_index = 1; break; case 212: tag_transform_script = optarg; break; + case 213: + if (strcmp(optarg,"none") == 0) cluster = 0; + else if (strcmp(optarg,"gist") == 0) cluster = 1; + else if (strcmp(optarg,"geohash") == 0) cluster = 2; + else {fprintf(stderr, "ERROR: Unrecognized cluster strategy %s.\n", optarg); exit(EXIT_FAILURE); } + break; case 'V': exit(EXIT_SUCCESS); case '?': default: @@ -615,6 +636,7 @@ int main(int argc, char *argv[]) } if (num_procs < 1) num_procs = 1; + if (num_threads < 1) num_threads = 1; if (pass_prompt) password = simple_prompt("Password:", 100, 0); @@ -680,13 +702,17 @@ int main(int argc, char *argv[]) options.parallel_indexing = parallel_indexing; options.alloc_chunkwise = alloc_chunkwise; options.num_procs = num_procs; + options.num_threads = num_threads; options.droptemp = droptemp; options.unlogged = unlogged; + options.cluster = cluster; options.flat_node_cache_enabled = flat_node_cache_enabled; options.flat_node_file = flat_nodes_file; options.excludepoly = excludepoly; options.tag_transform_script = tag_transform_script; + setDeduplicateStrings(!slim); + if (strcmp("pgsql", output_backend) == 0) { osmdata.out = &out_pgsql; } else if (strcmp("gazetteer", output_backend) == 0) { diff --git a/output-gazetteer.c b/output-gazetteer.c index 424c8bee6..631ea9125 100644 --- a/output-gazetteer.c +++ b/output-gazetteer.c @@ -67,6 +67,9 @@ static PGconn *ConnectionError = NULL; static FILE * hLog = NULL; +static void * middle_ctx; +static void * geom_ctx; + static void require_slim_mode(void) { if (!Options->slim) @@ -1042,7 +1045,9 @@ static int gazetteer_out_start(const struct output_options *options) } /* Setup middle layer */ - options->mid->start(options); + middle_ctx = options->mid->start(options); + + geom_ctx = init_geometry_ctx(); hLog = fopen("log", "w"); @@ -1054,7 +1059,7 @@ static void gazetteer_out_stop(void) /* Process any remaining ways and relations */ /* No longer need to access middle layer */ - Options->mid->commit(); + Options->mid->commit(middle_ctx); Options->mid->stop(); /* Stop any active copy */ @@ -1099,7 +1104,7 @@ static int gazetteer_process_node(osmid_t id, double lat, double lon, struct key split_tags(tags, TAGINFO_NODE, &names, &places, &extratags, &adminlevel, &housenumber, &street, &addr_place, &isin, &postcode, &countrycode); /* Feed this node to the middle layer */ - Options->mid->nodes_set(id, lat, lon, tags); + Options->mid->nodes_set(middle_ctx, id, lat, lon, tags); if (delete_old) delete_unused_classes('N', id, &places); @@ -1154,7 +1159,7 @@ static int gazetteer_process_way(osmid_t id, osmid_t *ndv, int ndc, struct keyva area = split_tags(tags, TAGINFO_WAY, &names, &places, &extratags, &adminlevel, &housenumber, &street, &addr_place, &isin, &postcode, &countrycode); /* Feed this way to the middle layer */ - Options->mid->ways_set(id, ndv, ndc, tags, 0); + Options->mid->ways_set(middle_ctx, id, ndv, ndc, tags, 0); if (delete_old) delete_unused_classes('W', id, &places); @@ -1168,7 +1173,7 @@ static int gazetteer_process_way(osmid_t id, osmid_t *ndv, int ndc, struct keyva /* Fetch the node details */ nodev = malloc(ndc * sizeof(struct osmNode)); - nodec = Options->mid->nodes_get_list(nodev, ndv, ndc); + nodec = Options->mid->nodes_get_list(middle_ctx, nodev, ndv, ndc); /* Get the geometry of the object */ if ((wkt = get_wkt_simple(nodev, nodec, area)) != NULL && strlen(wkt) > 0) @@ -1230,7 +1235,7 @@ static int gazetteer_process_relation(osmid_t id, struct member *members, int me if (!strcmp(type, "associatedStreet") || !strcmp(type, "relatedStreet")) { - Options->mid->relations_set(id, members, member_count, tags); + Options->mid->relations_set(middle_ctx, id, members, member_count, tags); if (delete_old) delete_unused_classes('R', id, 0); return 0; } @@ -1240,7 +1245,7 @@ static int gazetteer_process_relation(osmid_t id, struct member *members, int me return 0; } - Options->mid->relations_set(id, members, member_count, tags); + Options->mid->relations_set(middle_ctx, id, members, member_count, tags); /* Split the tags */ split_tags(tags, TAGINFO_AREA, &names, &places, &extratags, &adminlevel, &housenumber, &street, &addr_place, &isin, &postcode, &countrycode); @@ -1268,15 +1273,15 @@ static int gazetteer_process_relation(osmid_t id, struct member *members, int me count++; } - count = Options->mid->ways_get_list(xid2, count, &xid, xtags, xnodes, xcount); + count = Options->mid->ways_get_list(middle_ctx, xid2, count, &xid, xtags, xnodes, xcount); xnodes[count] = NULL; xcount[count] = 0; - wkt_size = build_geometry(id, xnodes, xcount, 1, 1, 1000000); + wkt_size = build_geometry(geom_ctx, id, xnodes, xcount, 1, 1, 1000000); for (i=0;imid->nodes_delete(id); + Options->mid->nodes_delete(middle_ctx, id); return 0; } @@ -1348,7 +1353,7 @@ static int gazetteer_delete_way(osmid_t id) delete_place('W', id); /* Feed this delete to the middle layer */ - Options->mid->ways_delete(id); + Options->mid->ways_delete(middle_ctx, id); return 0; } @@ -1362,7 +1367,7 @@ static int gazetteer_delete_relation(osmid_t id) delete_place('R', id); /* Feed this delete to the middle layer */ - Options->mid->relations_delete(id); + Options->mid->relations_delete(middle_ctx, id); return 0; } @@ -1370,21 +1375,21 @@ static int gazetteer_delete_relation(osmid_t id) static int gazetteer_modify_node(osmid_t id, double lat, double lon, struct keyval *tags) { require_slim_mode(); - Options->mid->nodes_delete(id); + Options->mid->nodes_delete(middle_ctx, id); return gazetteer_process_node(id, lat, lon, tags, 1); } static int gazetteer_modify_way(osmid_t id, osmid_t *ndv, int ndc, struct keyval *tags) { require_slim_mode(); - Options->mid->ways_delete(id); + Options->mid->ways_delete(middle_ctx, id); return gazetteer_process_way(id, ndv, ndc, tags, 1); } static int gazetteer_modify_relation(osmid_t id, struct member *members, int member_count, struct keyval *tags) { require_slim_mode(); - Options->mid->relations_delete(id); + Options->mid->relations_delete(middle_ctx, id); return gazetteer_process_relation(id, members, member_count, tags, 1); } diff --git a/output-pgsql.c b/output-pgsql.c index a9e55a448..39908f743 100644 --- a/output-pgsql.c +++ b/output-pgsql.c @@ -59,13 +59,13 @@ static struct s_table { unsigned int buflen; int copyMode; char *columns; -} tables [] = { +} global_tables [] = { { .name = "%s_point", .type = "POINT" }, { .name = "%s_line", .type = "LINESTRING"}, { .name = "%s_polygon", .type = "GEOMETRY" }, /* Actually POLGYON & MULTIPOLYGON but no way to limit to just these two */ { .name = "%s_roads", .type = "LINESTRING"} }; -#define NUM_TABLES ((signed)(sizeof(tables) / sizeof(tables[0]))) +#define NUM_TABLES ((signed)(sizeof(global_tables) / sizeof(global_tables[0]))) static struct flagsname { @@ -85,9 +85,51 @@ static struct flagsname { struct taginfo *exportList[4]; /* Indexed by enum table_id */ int exportListCount[4]; -static int pgsql_delete_way_from_output(osmid_t osm_id); + +struct thread_ctx { + void * geom_ctx; + void * tagtransform_ctx; + void * middle_ctx; + struct s_table * tables; +}; + +static struct thread_ctx global_ctx; + +#ifdef HAVE_PTHREAD +/** + * Data structure to pass work from the main thread to the worker threads. This + * allows to de-synchronize the threads with the main thread feeding work into the + * pipe and the worker threads then taking out one element at a time and processing it. + * It is a circular buffer storing up to 64 entries for each way and relation + */ +#define WORKER_THREAD_QUEUE_SIZE 64 +static struct relation_info2 * rels_buffer[WORKER_THREAD_QUEUE_SIZE]; +static struct way_info2 * ways_buffer[WORKER_THREAD_QUEUE_SIZE]; +static rels_buffer_pfree = 0; //Pointer to the first free slot in the buffer +static rels_buffer_pfirst = 0;//Pointer to the first full slot in the buffer +static ways_buffer_pfree = 0; //Pointer to the first free slot in the buffer +static ways_buffer_pfirst = 0;//Pointer to the first full slot in the buffer + + +static pthread_t * worker_threads = NULL; +pthread_mutex_t lock_worker_queue = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cond_worker_queue_work_available; +pthread_cond_t cond_worker_queue_space_available; +pthread_cond_t cond_worker_flush_middle; //Used to synchronize committing of middle layer in all threads on switching between ways and relations + +static uint64_t way_inflight; //signals which threads have written ways to the middle layer and need to be flushed before switching to relations + +//pthread_mutex_t lock_middle_processing = PTHREAD_MUTEX_INITIALIZER; + +static volatile workers_finish = 0; +#endif + +static int pgsql_delete_way_from_output(osmid_t osm_id, struct s_table * tables); static int pgsql_delete_relation_from_output(osmid_t osm_id); static int pgsql_process_relation(osmid_t id, struct member *members, int member_count, struct keyval *tags, int exists); +static int pgsql_out_connect2(const struct output_options *options, struct s_table * tables, int startTransaction); +static void pgsql_out_close2(int stopTransaction, struct s_table * tables); +static void pgsql_pause_copy(struct s_table *table); void read_style_file( const char *filename ) { @@ -227,29 +269,29 @@ static void free_style(void) * with most empty and one byte delimiters, without this optimisation we * transfer three times the amount of data necessary. */ -void copy_to_table(enum table_id table, const char *sql) +static void copy_to_table(struct s_table * table, const char *sql) { - PGconn *sql_conn = tables[table].sql_conn; + PGconn *sql_conn = table->sql_conn; unsigned int len = strlen(sql); - unsigned int buflen = tables[table].buflen; - char *buffer = tables[table].buffer; + unsigned int buflen = table->buflen; + char *buffer = table->buffer; /* Return to copy mode if we dropped out */ - if( !tables[table].copyMode ) + if( !table->copyMode ) { - pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s (%s,way) FROM STDIN", tables[table].name, tables[table].columns); - tables[table].copyMode = 1; + pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s (%s,way) FROM STDIN", table->name, table->columns); + table->copyMode = 1; } /* If the combination of old and new data is too big, flush old data */ - if( (unsigned)(buflen + len) > sizeof( tables[table].buffer )-10 ) + if( (unsigned)(buflen + len) > sizeof( table->buffer )-10 ) { - pgsql_CopyData(tables[table].name, sql_conn, buffer); + pgsql_CopyData(table->name, sql_conn, buffer); buflen = 0; /* If new data by itself is also too big, output it immediately */ - if( (unsigned)len > sizeof( tables[table].buffer )-10 ) + if( (unsigned)len > sizeof( table->buffer )-10 ) { - pgsql_CopyData(tables[table].name, sql_conn, sql); + pgsql_CopyData(table->name, sql_conn, sql); len = 0; } } @@ -264,18 +306,19 @@ void copy_to_table(enum table_id table, const char *sql) /* If we have completed a line, output it */ if( buflen > 0 && buffer[buflen-1] == '\n' ) { - pgsql_CopyData(tables[table].name, sql_conn, buffer); + pgsql_CopyData(table->name, sql_conn, buffer); buflen = 0; } - tables[table].buflen = buflen; + table->buflen = buflen; } -static void pgsql_out_cleanup(void) +static void pgsql_out_cleanup(void * tables_p) { + struct s_table * tables = tables_p; int i; for (i=0; i tmplen) { - tmpstr=realloc(tmpstr,len); - tmplen=len; - } - strcpy(tmpstr,value); - - if ( !strcmp(type, "int4") ) { - int from, to; - /* For integers we take the first number, or the average if it's a-b */ - items = sscanf(value, "%d-%d", &from, &to); - if ( items == 1 ) { - sprintf(sql, "%d", from); - } else if ( items == 2 ) { - sprintf(sql, "%d", (from + to) / 2); - } else { - sprintf(sql, "\\N"); - } - } else { - /* - try to "repair" real values as follows: - * assume "," to be a decimal mark which need to be replaced by "." - * like int4 take the first number, or the average if it's a-b - * assume SI unit (meters) - * convert feet to meters (1 foot = 0.3048 meters) - * reject anything else - */ - if ( !strcmp(type, "real") ) { - int i,slen; - float from,to; - - slen=strlen(value); - for (i=0;inext->key != NULL) @@ -403,15 +444,15 @@ static void write_hstore(enum table_id table, struct keyval *tags) /* finish the hstore column by placing a TAB into the data stream */ copy_to_table(table, "\t"); - + free(sql); /* the main hstore-column has now been written */ } /* write an hstore column to the database */ -static void write_hstore_columns(enum table_id table, struct keyval *tags) +static void write_hstore_columns(struct s_table * table, struct keyval *tags) { - static char *sql; - static size_t sqllen=0; + char *sql; + int sqllen=0; char *shortkey; /* the index of the current hstore column */ int i_hstore_column; @@ -421,10 +462,8 @@ static void write_hstore_columns(enum table_id table, struct keyval *tags) size_t hlen; /* sql buffer */ - if (sqllen==0) { - sqllen=2048; - sql=malloc(sqllen); - } + sqllen=2048; + sql=malloc(sqllen); /* iterate over all configured hstore colums in the options */ for(i_hstore_column = 0; i_hstore_column < Options->n_hstore_columns; i_hstore_column++) @@ -486,6 +525,8 @@ static void write_hstore_columns(enum table_id table, struct keyval *tags) copy_to_table(table, "\t"); } + free(sql); + /* all hstore-columns have now been written */ } @@ -506,7 +547,7 @@ Workaround - output SRID=4326; static int pgsql_out_node(osmid_t id, struct keyval *tags, double node_lat, double node_lon) { - int filter = tagtransform_filter_node_tags(tags); + int filter = tagtransform_filter_node_tags(global_ctx.tagtransform_ctx, tags); static char *sql; static size_t sqllen=0; int i; @@ -521,7 +562,7 @@ static int pgsql_out_node(osmid_t id, struct keyval *tags, double node_lat, doub expire_tiles_from_bbox(node_lon, node_lat, node_lon, node_lat); sprintf(sql, "%" PRIdOSMID "\t", id); - copy_to_table(t_point, sql); + copy_to_table(&(global_tables[t_point]), sql); for (i=0; i < exportListCount[OSMTYPE_NODE]; i++) { if( exportList[OSMTYPE_NODE][i].flags & FLAG_DELETE ) @@ -538,16 +579,16 @@ static int pgsql_out_node(osmid_t id, struct keyval *tags, double node_lat, doub else sprintf(sql, "\\N"); - copy_to_table(t_point, sql); - copy_to_table(t_point, "\t"); + copy_to_table(&(global_tables[t_point]), sql); + copy_to_table(&(global_tables[t_point]), "\t"); } /* hstore columns */ - write_hstore_columns(t_point, tags); + write_hstore_columns(&(global_tables[t_point]), tags); /* check if a regular hstore is requested */ if (Options->enable_hstore) - write_hstore(t_point, tags); + write_hstore(&(global_tables[t_point]), tags); #ifdef FIXED_POINT // guarantee that we use the same values as in the node cache @@ -557,26 +598,24 @@ static int pgsql_out_node(osmid_t id, struct keyval *tags, double node_lat, doub #endif sprintf(sql, "SRID=%d;POINT(%.15g %.15g)", SRID, node_lon, node_lat); - copy_to_table(t_point, sql); - copy_to_table(t_point, "\n"); + copy_to_table(&(global_tables[t_point]), sql); + copy_to_table(&(global_tables[t_point]), "\n"); return 0; } -static void write_wkts(osmid_t id, struct keyval *tags, const char *wkt, enum table_id table) +static void write_wkts(osmid_t id, struct keyval *tags, const char *wkt, struct s_table * table) { - static char *sql; - static size_t sqllen=0; + char *sql; + size_t sqllen=0; int j; struct keyval *tag; - if (sqllen==0) { - sqllen=2048; - sql=malloc(sqllen); - } + sqllen=2048; + sql=malloc(sqllen); sprintf(sql, "%" PRIdOSMID "\t", id); copy_to_table(table, sql); @@ -611,6 +650,7 @@ static void write_wkts(osmid_t id, struct keyval *tags, const char *wkt, enum ta copy_to_table(table, sql); copy_to_table(table, wkt); copy_to_table(table, "\n"); + free(sql); } /*static int tag_indicates_polygon(enum OsmType type, const char *key) @@ -638,126 +678,204 @@ E4C1421D5BF24D06053E7DF4940 212696 Oswald Road \N \N \N \N \N \N minor \N \N \N \N \N \N \N 0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D 5BF5BB39597FCDF4940 */ -static int pgsql_out_way(osmid_t id, struct keyval *tags, struct osmNode *nodes, int count, int exists) -{ +static int pgsql_out_way_single(struct thread_ctx * ctx, struct way_info * way) { int polygon = 0, roads = 0; int i, wkt_size; double split_at; double area; /* If the flag says this object may exist already, delete it first */ - if(exists) { - pgsql_delete_way_from_output(id); - Options->mid->way_changed(id); + if (way->exists) { + pgsql_delete_way_from_output(way->id, ctx->tables); + Options->mid->way_changed(ctx->middle_ctx, way->id); } - if (tagtransform_filter_way_tags(tags, &polygon, &roads)) + if (tagtransform_filter_way_tags(ctx->tagtransform_ctx, way->tags, &polygon, &roads)) { + resetList(way->tags); + free(way->tags); + free(way->nodes); + free(way); return 0; + } /* Split long ways after around 1 degree or 100km */ if (Options->projection == PROJ_LATLONG) split_at = 1; else split_at = 100 * 1000; - wkt_size = get_wkt_split(nodes, count, polygon, split_at); + wkt_size = get_wkt_split(ctx->geom_ctx, way->nodes, way->node_count, polygon, + split_at); - for (i=0;igeom_ctx, i); if (wkt && strlen(wkt)) { /* FIXME: there should be a better way to detect polygons */ - if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) { - expire_tiles_from_nodes_poly(nodes, count, id); - area = get_area(i); + if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) + || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) { + expire_tiles_from_nodes_poly(way->nodes, way->node_count, + way->id); + area = get_area(ctx->geom_ctx, i); if ((area > 0.0) && enable_way_area) { char tmp[32]; snprintf(tmp, sizeof(tmp), "%g", area); - addItem(tags, "way_area", tmp, 0); + addItem(way->tags, "way_area", tmp, 0); } - write_wkts(id, tags, wkt, t_poly); + write_wkts(way->id, way->tags, wkt, &(ctx->tables[t_poly])); } else { - expire_tiles_from_nodes_line(nodes, count); - write_wkts(id, tags, wkt, t_line); - if (roads) - write_wkts(id, tags, wkt, t_roads); + expire_tiles_from_nodes_line(way->nodes, way->node_count); + write_wkts(way->id, way->tags, wkt, &(ctx->tables[t_line])); + if (roads) { + write_wkts(way->id, way->tags, wkt, &(ctx->tables[t_roads])); + } } } free(wkt); } - clear_wkts(); - + clear_wkts(ctx->geom_ctx); + + resetList(way->tags); + free(way->tags); + free(way->nodes); + free(way); + return 0; } -static int pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int member_count, struct osmNode **xnodes, struct keyval *xtags, int *xcount, osmid_t *xid, const char **xrole) -{ - int i, wkt_size; - int roads = 0; +static int pgsql_add_way_single(struct thread_ctx * ctx, struct way_info2 * way) { + int polygon = 0; + int roads = 0; + struct way_info * way_full; + + + /* Check whether the way is: (1) Exportable, (2) Maybe a polygon */ + int filter = tagtransform_filter_way_tags(ctx->tagtransform_ctx, way->tags, &polygon, &roads); + + /* If this isn't a polygon then it can not be part of a multipolygon + Hence only polygons are "pending" */ + Options->mid->ways_set(ctx->middle_ctx, way->id, way->nds, way->nd_count, way->tags, (!filter && polygon) ? 1 : 0); + + if( !polygon && !filter ) + { + /* Get actual node data and generate output */ + struct osmNode *nodes = malloc( sizeof(struct osmNode) * way->nd_count ); + int count = Options->mid->nodes_get_list(ctx->middle_ctx, nodes, way->nds, way->nd_count ); + way_full = (struct way_info *)malloc(sizeof(struct way_info)); + way_full->id = way->id; + way_full->tags = way->tags; + way_full->exists = 0; + way_full->node_count = count; + way_full->nodes = nodes; + pgsql_out_way_single(ctx, way_full); + + } else { + resetList(way->tags); + free(way->tags); + } + free(way->nds); + free(way); + + return 0; +} + + +static void free_rel_struct(struct relation_info * rel) { + int i; + for( i =0; imember_count; i++ ) { + resetList( &(rel->member_tags[i]) ); + free( rel->member_way_nodes[i] ); + free( rel->member_roles[i]); + } + + if (rel->member_ids) free(rel->member_ids); + if (rel->member_tags) free(rel->member_tags); + if (rel->member_way_node_count) free(rel->member_way_node_count); + if (rel->member_way_nodes) free(rel->member_way_nodes); + if (rel->member_roles) free(rel->member_roles); + resetList(rel->tags); + free(rel->tags); + free(rel); +} + + + +static int pgsql_out_relation_single(struct relation_info * rel, struct thread_ctx * ctx) { + int * members_superseeded; int make_polygon = 0; int make_boundary = 0; - int * members_superseeded; + int polygon = 0, roads = 0; + char *type; double split_at; + int i, wkt_size; - members_superseeded = calloc(sizeof(int), member_count); + members_superseeded = calloc(sizeof(int), rel->member_count); - if (member_count == 0) { + if (rel->member_count == 0) { free(members_superseeded); + free_rel_struct(rel); return 0; } - if (tagtransform_filter_rel_member_tags(rel_tags, member_count, xtags, xrole, members_superseeded, &make_boundary, &make_polygon, &roads)) { + if (tagtransform_filter_rel_member_tags(ctx->tagtransform_ctx, rel->tags, rel->member_count, + rel->member_tags, rel->member_roles, members_superseeded, + &make_boundary, &make_polygon, &roads)) { free(members_superseeded); + free_rel_struct(rel); return 0; } - + /* Split long linear ways after around 1 degree or 100km (polygons not effected) */ if (Options->projection == PROJ_LATLONG) split_at = 1; else split_at = 100 * 1000; - wkt_size = build_geometry(id, xnodes, xcount, make_polygon, Options->enable_multi, split_at); + wkt_size = build_geometry(ctx->geom_ctx, rel->id, rel->member_way_nodes, + rel->member_way_node_count, make_polygon, Options->enable_multi, + split_at); if (!wkt_size) { free(members_superseeded); + free_rel_struct(rel); return 0; } - for (i=0;igeom_ctx, i); if (wkt && strlen(wkt)) { - expire_tiles_from_wkt(wkt, -id); + expire_tiles_from_wkt(wkt, -rel->id); /* FIXME: there should be a better way to detect polygons */ - if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) { - double area = get_area(i); + if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) + || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) { + double area = get_area(ctx->geom_ctx, i); if ((area > 0.0) && enable_way_area) { char tmp[32]; snprintf(tmp, sizeof(tmp), "%g", area); - addItem(rel_tags, "way_area", tmp, 0); + addItem(rel->tags, "way_area", tmp, 0); } - write_wkts(-id, rel_tags, wkt, t_poly); + write_wkts(-rel->id, rel->tags, wkt, &(ctx->tables[t_poly])); } else { - write_wkts(-id, rel_tags, wkt, t_line); + write_wkts(-rel->id, rel->tags, wkt, &(ctx->tables[t_line])); if (roads) - write_wkts(-id, rel_tags, wkt, t_roads); + write_wkts(-rel->id, rel->tags, wkt, &(ctx->tables[t_roads])); } } free(wkt); } - clear_wkts(); + clear_wkts(ctx->geom_ctx); /* Tagtransform will have marked those member ways of the relation that * have fully been dealt with as part of the multi-polygon entry. * Set them in the database as done and delete their entry to not * have duplicates */ if (make_polygon) { - for (i=0; xcount[i]; i++) { + for (i = 0; rel->member_way_node_count[i]; i++) { if (members_superseeded[i]) { - Options->mid->ways_done(xid[i]); - pgsql_delete_way_from_output(xid[i]); + //TODO: Need to find a thread-safe way to do the done marking + Options->mid->ways_done(ctx->middle_ctx, rel->member_ids[i]); + pgsql_delete_way_from_output(rel->member_ids[i], ctx->tables); } } } @@ -765,35 +883,217 @@ static int pgsql_out_relation(osmid_t id, struct keyval *rel_tags, int member_co free(members_superseeded); /* If we are making a boundary then also try adding any relations which form complete rings - The linear variants will have already been processed above */ + The linear variants will have already been processed above */ if (make_boundary) { - wkt_size = build_geometry(id, xnodes, xcount, 1, Options->enable_multi, split_at); - for (i=0;igeom_ctx, rel->id, rel->member_way_nodes, + rel->member_way_node_count, 1, Options->enable_multi, split_at); + for (i = 0; i < wkt_size; i++) { + char *wkt = get_wkt(ctx->geom_ctx, i); if (strlen(wkt)) { - expire_tiles_from_wkt(wkt, -id); + expire_tiles_from_wkt(wkt, -rel->id); /* FIXME: there should be a better way to detect polygons */ - if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) { - double area = get_area(i); + if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) + || !strncmp(wkt, "MULTIPOLYGON", + strlen("MULTIPOLYGON"))) { + double area = get_area(ctx->geom_ctx, i); if ((area > 0.0) && enable_way_area) { char tmp[32]; snprintf(tmp, sizeof(tmp), "%g", area); - addItem(rel_tags, "way_area", tmp, 0); + addItem(rel->tags, "way_area", tmp, 0); } - write_wkts(-id, rel_tags, wkt, t_poly); + write_wkts(-rel->id, rel->tags, wkt, &ctx->tables[t_poly]); } } free(wkt); } - clear_wkts(); + clear_wkts(ctx->geom_ctx); } - return 0; + free_rel_struct(rel); + + return 1; } -static int pgsql_out_connect(const struct output_options *options, int startTransaction) { +/* This is the workhorse of pgsql_add_relation, split out because it is used as the callback for iterate relations */ +static int pgsql_process_relation_single(struct thread_ctx * ctx, struct relation_info2 * rel) { + struct relation_info * rel_full = calloc(1,sizeof(struct relation_info)); + int i, j, count; + osmid_t *xid2 = malloc( (rel->member_count+1) * sizeof(osmid_t) ); + int filter; + + rel_full->id = rel->id; + rel_full->tags = rel->tags; + + rel_full->member_roles = malloc( (rel->member_count+1) * sizeof(const char *) ); + rel_full->member_way_node_count = malloc( (rel->member_count+1) * sizeof(int) ); + rel_full->member_tags = malloc( (rel->member_count+1) * sizeof(struct keyval) ); + rel_full->member_way_nodes = malloc( (rel->member_count+1) * sizeof(struct osmNode*) ); + + /* If the flag says this object may exist already, delete it first */ + if(rel->exists) + pgsql_delete_relation_from_output(rel->id); + + if (tagtransform_filter_rel_tags(ctx->tagtransform_ctx, rel->tags)) { + free_rel_struct(rel_full); + return 1; + } + + count = 0; + for( i=0; i < rel->member_count; i++ ) { + /* Need to handle more than just ways... */ + if( rel->members[i].type != OSMTYPE_WAY ) + continue; + xid2[count] = rel->members[i].id; + count++; + } + + rel_full->member_count = Options->mid->ways_get_list(ctx->middle_ctx, xid2, count, &rel_full->member_ids, rel_full->member_tags, rel_full->member_way_nodes, rel_full->member_way_node_count); + + for (i = 0; i < rel_full->member_count; i++) { + for (j = i; j < rel->member_count; j++) { + if (rel->members[j].id == rel_full->member_ids[i]) break; + } + rel_full->member_roles[i] = strdup(rel->members[j].role); + } + rel_full->member_way_nodes[rel_full->member_count] = NULL; + rel_full->member_way_node_count[rel_full->member_count] = 0; + rel_full->member_ids[rel_full->member_count] = 0; + rel_full->member_roles[rel_full->member_count] = NULL; + + for (i = 0; i < rel->member_count; i++) { + free(rel->members[i].role); + } + free(rel->members); + free(rel); + + /* At some point we might want to consider storing the retrieved data in the members, rather than as separate arrays */ + pgsql_out_relation_single(rel_full, ctx); + + free(xid2); + return 0; +} + +/** + * This is the thread function for the worker threads. It takes an element from the work queue and passes it on to the + * actual processing function. + */ +#ifdef HAVE_PTHREAD +static void * pgsql_worker_thread(void * pointer) { + struct relation_info2 * rel; + struct way_info2 * way; + struct thread_ctx ctx; + int * thread_id = pointer; + + + /* + * We need a new set of connections to postgresql in this thread + */ + ctx.tables = malloc(sizeof(global_tables)); + memcpy(ctx.tables, global_tables, sizeof(global_tables)); + pgsql_out_connect2(Options, ctx.tables, 0); + + ctx.geom_ctx = init_geometry_ctx(); //create a new geometry ctx for this thread + ctx.tagtransform_ctx = tagtransform_init(Options); + if (Options->mid->connect) { + ctx.middle_ctx = Options->mid->connect(Options); + } else ctx.middle_ctx = global_ctx.middle_ctx; + + while ((workers_finish == 0) || (ways_buffer_pfirst != ways_buffer_pfree) || (rels_buffer_pfirst != rels_buffer_pfree)) { + pthread_mutex_lock(&lock_worker_queue); + while ((ways_buffer_pfirst == ways_buffer_pfree) && (rels_buffer_pfirst == rels_buffer_pfree)) { + pthread_cond_wait(&cond_worker_queue_work_available, &lock_worker_queue); + if (workers_finish) { //We are done and trying to exit. + pthread_mutex_unlock(&lock_worker_queue); + break; + } + } + //If we exited the while loop without there actually being work, then presumably we are done and want to exit the thread. + if ((ways_buffer_pfirst == ways_buffer_pfree) && (rels_buffer_pfirst == rels_buffer_pfree)) continue; + + way = NULL; + rel = NULL; + if (ways_buffer_pfirst != ways_buffer_pfree) { //We have an element in the way queue, process it. + way = ways_buffer[ways_buffer_pfirst]; + ways_buffer_pfirst++; + if (ways_buffer_pfirst > (WORKER_THREAD_QUEUE_SIZE - 1)) ways_buffer_pfirst = 0; // circular buffer wrap around + way_inflight |= ((uint64_t)1 << *thread_id); //We will likely have written a way to the middle layer, so we need to remember to flush it before moving on. + } else { + rel = rels_buffer[rels_buffer_pfirst]; + rels_buffer_pfirst++; + if (rels_buffer_pfirst > (WORKER_THREAD_QUEUE_SIZE - 1)) rels_buffer_pfirst = 0; // circular buffer wrap around + } + pthread_mutex_unlock(&lock_worker_queue); + pthread_cond_signal(&cond_worker_queue_space_available); + if (way) pgsql_add_way_single(&ctx, way); + if (rel) { + /* Before processing relations, we need to make sure that all ways in the middle layer have been + * committed and visible to all threads, as otherwise we might not be able to retrieve all ways in + * the relation processing. + */ + if ((way_inflight & ((uint64_t)1 << *thread_id)) > 0) { //Check if this thread needs committing + Options->mid->commit(ctx.middle_ctx); + pthread_mutex_lock(&lock_worker_queue); + way_inflight ^= ((uint64_t)1 << *thread_id); + if (way_inflight == 0) { + pthread_cond_broadcast(&cond_worker_flush_middle); + } + pthread_mutex_unlock(&lock_worker_queue); + } + pthread_mutex_lock(&lock_worker_queue); + while (way_inflight) { + pthread_cond_wait(&cond_worker_flush_middle, &lock_worker_queue); + } + pthread_mutex_unlock(&lock_worker_queue); + pgsql_process_relation_single(&ctx, rel); + } + + } + //We are done, closing worker thread. + Options->mid->commit(ctx.middle_ctx); + Options->mid->cleanup(ctx.middle_ctx); + pgsql_out_close2(0, ctx.tables); + free(ctx.tables); + close_geometry_ctx(ctx.geom_ctx); + tagtransform_shutdown(ctx.tagtransform_ctx); + free(thread_id); + return NULL; +} +#endif //HAVE_PTHREAD + +static int pgsql_out_way(osmid_t id, struct keyval *tags, struct osmNode *nodes, int count, int exists) { + int i; + struct way_info * way; + //Create a worker package to put in the queue. + way = (struct way_info *)malloc(sizeof(struct way_info)); + way->id = id; + /* We need to duplicate the tags structure here, as the calling function of pgsql_out_way + * currently assumes it can free the tags. However, they can only be freed once the worker + * threads have actually finished the processing after which they will free the tags structure + * of the work package. + */ + way->tags = malloc(sizeof(struct keyval)); + initList(way->tags); + cloneList(way->tags, tags); + way->nodes = nodes; + way->node_count = count; + way->exists = exists; + + /* The external callers of pgsql_out_way assume they can free the nodes + * array. However due to the asynchronisity of the worker thread model, + * the pgsql_out_way_single function frees the nodes array. Due to the + * mismatch in the API, duplicate the nodes array, that both caller and + * pgsql_out_way_single can free the array. + * TODO: Find a better way, by fixing upstream of pgsql_out_way() + */ + + way->nodes = malloc(sizeof(struct osmNode) * count); + memcpy(way->nodes, nodes, sizeof(struct osmNode) * count); + return pgsql_out_way_single(&global_ctx, way); +} + + +static int pgsql_out_connect2(const struct output_options *options, struct s_table * tables, int startTransaction) { int i; for (i=0; iprefix) + strlen(tables[i].name) + 1 ); - sprintf( temp, tables[i].name, options->prefix ); - tables[i].name = temp; + char *temp = malloc( strlen(options->prefix) + strlen(global_tables[i].name) + 1 ); + sprintf( temp, global_tables[i].name, options->prefix ); + global_tables[i].name = temp; } - fprintf(stderr, "Setting up table: %s\n", tables[i].name); + fprintf(stderr, "Setting up table: %s\n", global_tables[i].name); sql_conn = PQconnectdb(options->conninfo); /* Check to see that the backend connection was successfully made */ @@ -850,40 +1157,40 @@ static int pgsql_out_start(const struct output_options *options) fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn)); exit_nicely(); } - tables[i].sql_conn = sql_conn; + global_tables[i].sql_conn = sql_conn; pgsql_exec(sql_conn, PGRES_COMMAND_OK, "SET synchronous_commit TO off;"); if (!options->append) { - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s", tables[i].name); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s", global_tables[i].name); } else { - sprintf(sql, "SELECT srid FROM geometry_columns WHERE f_table_name='%s';", tables[i].name); + sprintf(sql, "SELECT srid FROM geometry_columns WHERE f_table_name='%s';", global_tables[i].name); res = PQexec(sql_conn, sql); if (!((PQntuples(res) == 1) && (PQnfields(res) == 1))) { - fprintf(stderr, "Problem reading geometry information for table %s - does it exist?\n", tables[i].name); + fprintf(stderr, "Problem reading geometry information for table %s - does it exist?\n", global_tables[i].name); exit_nicely(); } their_srid = atoi(PQgetvalue(res, 0, 0)); PQclear(res); if (their_srid != SRID) { - fprintf(stderr, "SRID mismatch: cannot append to table %s (SRID %d) using selected SRID %d\n", tables[i].name, their_srid, SRID); + fprintf(stderr, "SRID mismatch: cannot append to table %s (SRID %d) using selected SRID %d\n", global_tables[i].name, their_srid, SRID); exit_nicely(); } } /* These _tmp tables can be left behind if we run out of disk space */ - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s_tmp", tables[i].name); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s_tmp", global_tables[i].name); - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "BEGIN"); + //pgsql_exec(sql_conn, PGRES_COMMAND_OK, "BEGIN"); type = (i == t_point)?OSMTYPE_NODE:OSMTYPE_WAY; numTags = exportListCount[type]; exportTags = exportList[type]; if (!options->append) { - sprintf(sql, "CREATE TABLE %s ( osm_id " POSTGRES_OSMID_TYPE, tables[i].name ); + sprintf(sql, "CREATE TABLE %s ( osm_id " POSTGRES_OSMID_TYPE, global_tables[i].name ); for (j=0; j < numTags; j++) { if( exportTags[j].flags & FLAG_DELETE ) continue; @@ -914,23 +1221,23 @@ static int pgsql_out_start(const struct output_options *options) pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", sql); pgsql_exec(sql_conn, PGRES_TUPLES_OK, "SELECT AddGeometryColumn('%s', 'way', %d, '%s', 2 );\n", - tables[i].name, SRID, tables[i].type ); - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ALTER COLUMN way SET NOT NULL;\n", tables[i].name); + global_tables[i].name, SRID, global_tables[i].type ); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ALTER COLUMN way SET NOT NULL;\n", global_tables[i].name); /* slim mode needs this to be able to apply diffs */ if (Options->slim && !Options->droptemp) { - sprintf(sql, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id)", tables[i].name, tables[i].name); - if (Options->tblsmain_index) { - sprintf(sql + strlen(sql), " TABLESPACE %s\n", Options->tblsmain_index); - } - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", sql); + sprintf(sql, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id)", global_tables[i].name, global_tables[i].name); + if (Options->tblsmain_index) { + sprintf(sql + strlen(sql), " TABLESPACE %s\n", Options->tblsmain_index); + } + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", sql); } } else { /* Add any new columns referenced in the default.style */ PGresult *res; - sprintf(sql, "SELECT * FROM %s LIMIT 0;\n", tables[i].name); + sprintf(sql, "SELECT * FROM %s LIMIT 0;\n", global_tables[i].name); res = PQexec(sql_conn, sql); if (PQresultStatus(res) != PGRES_TUPLES_OK) { - fprintf(stderr, "Error, failed to query table %s\n%s\n", tables[i].name, sql); + fprintf(stderr, "Error, failed to query table %s\n%s\n", global_tables[i].name, sql); exit_nicely(); } for (j=0; j < numTags; j++) { @@ -944,8 +1251,8 @@ static int pgsql_out_start(const struct output_options *options) fprintf(stderr, "Append failed. Column \"%s\" is missing from \"%s\"\n", exportTags[j].name, tables[i].name); exit_nicely(); #else - fprintf(stderr, "Adding new column \"%s\" to \"%s\"\n", exportTags[j].name, tables[i].name); - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ADD COLUMN \"%s\" %s;\n", tables[i].name, exportTags[j].name, exportTags[j].type); + fprintf(stderr, "Adding new column \"%s\" to \"%s\"\n", exportTags[j].name, global_tables[i].name); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ADD COLUMN \"%s\" %s;\n", global_tables[i].name, exportTags[j].name, exportTags[j].type); #endif } /* Note: we do not verify the type or delete unused columns */ @@ -955,7 +1262,7 @@ static int pgsql_out_start(const struct output_options *options) /* change the type of the geometry column if needed - this can only change to a more permisive type */ } - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "PREPARE get_wkt (" POSTGRES_OSMID_TYPE ") AS SELECT ST_AsText(way) FROM %s WHERE osm_id = $1;\n", tables[i].name); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "PREPARE get_wkt (" POSTGRES_OSMID_TYPE ") AS SELECT ST_AsText(way) FROM %s WHERE osm_id = $1;\n", global_tables[i].name); /* Generate column list for COPY */ strcpy(sql, "osm_id"); @@ -983,20 +1290,28 @@ static int pgsql_out_start(const struct output_options *options) if (Options->enable_hstore) strcat(sql,",tags"); - tables[i].columns = strdup(sql); - pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s (%s,way) FROM STDIN", tables[i].name, tables[i].columns); + global_tables[i].columns = strdup(sql); + pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s (%s,way) FROM STDIN", global_tables[i].name, global_tables[i].columns); - tables[i].copyMode = 1; + global_tables[i].copyMode = 1; } free(sql); - if (tagtransform_init(options)) { + global_ctx.tables = global_tables; + global_ctx.geom_ctx = init_geometry_ctx(); + global_ctx.tagtransform_ctx = tagtransform_init(options); + + if (!global_ctx.tagtransform_ctx) { fprintf(stderr, "Error: Failed to initialise tag processing.\n"); exit_nicely(); } expire_tiles_init(options); - options->mid->start(options); + global_ctx.middle_ctx = options->mid->start(options); + if (global_ctx.middle_ctx == NULL) { + fprintf(stderr, "Error: Failed to initialise the middle layer.\n"); + exit_nicely(); + } return 0; } @@ -1026,7 +1341,7 @@ static void pgsql_pause_copy(struct s_table *table) table->copyMode = 0; } -static void pgsql_out_close(int stopTransaction) { +static void pgsql_out_close2(int stopTransaction, struct s_table * tables) { int i; for (i=0; iappend) { - time_t start, end; + time_t start, end, start_command, end_command; time(&start); fprintf(stderr, "Sorting data and creating indexes for %s\n", table->name); pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ANALYZE %s;\n", table->name); - fprintf(stderr, "Analyzing %s finished\n", table->name); - if (Options->tblsmain_data) { - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE TABLE %s_tmp " - "TABLESPACE %s AS SELECT * FROM %s ORDER BY way;\n", - table->name, Options->tblsmain_data, table->name); - } else { - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE TABLE %s_tmp AS SELECT * FROM %s ORDER BY way;\n", table->name, table->name); + time(&end_command); + fprintf(stderr, "Analyzing %s finished. Took %li seconds\n", table->name, (end_command - start)); + + if (Options->cluster == 2) { + fprintf(stderr, "Creating geohash index on %s\n", table->name); + time(&start_command); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_geohash ON %s (st_geohash(st_transform(way,4326),15));\n", table->name, table->name); + time(&end_command); + fprintf(stderr, "Created geohash index for %s. Took %li seconds.\n", table->name, (end_command - start_command)); + fprintf(stderr, "Clustering on geohash index on %s\n", table->name); + time(&start_command); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CLUSTER %s USING %s_geohash;\n", table->name, table->name); + time(&end_command); + fprintf(stderr, "Clustered on geometry index for %s. Took %li seconds.\n", table->name, (end_command - start_command)); } - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE %s;\n", table->name); - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s_tmp RENAME TO %s;\n", table->name, table->name); - fprintf(stderr, "Copying %s to cluster by geometry finished\n", table->name); fprintf(stderr, "Creating geometry index on %s\n", table->name); + time(&start_command); if (Options->tblsmain_index) { /* Use fillfactor 100 for un-updatable imports */ if (Options->slim && !Options->droptemp) { @@ -1093,20 +1417,22 @@ static void *pgsql_out_stop_one(void *arg) pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_index ON %s USING GIST (way) WITH (FILLFACTOR=100);\n", table->name, table->name); } } - - /* slim mode needs this to be able to apply diffs */ - if (Options->slim && !Options->droptemp) - { - fprintf(stderr, "Creating osm_id index on %s\n", table->name); - if (Options->tblsmain_index) { - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id) TABLESPACE %s;\n", table->name, table->name, Options->tblsmain_index); - } else { - pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id);\n", table->name, table->name); - } + time(&end_command); + fprintf(stderr, "Created geometry index on %s. Took %li seconds.\n", table->name, (end_command - start_command)); + + fprintf(stderr,"Clustering: %i\n", Options->cluster ); + if (Options->cluster == 1) { + fprintf(stderr, "Clustering on geometry index for %s\n", table->name); + time(&start_command); + pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CLUSTER %s USING %s_index;\n", table->name, table->name); + time(&end_command); + fprintf(stderr, "Clustered on geometry index for %s. Took %li seconds.\n", table->name, (end_command - start_command)); } + /* Create hstore index if selected */ if (Options->enable_hstore_index) { fprintf(stderr, "Creating hstore indexes on %s\n", table->name); + time(&start_command); if (Options->tblsmain_index) { if (HSTORE_NONE != (Options->enable_hstore)) { if (Options->slim && !Options->droptemp) { @@ -1140,6 +1466,8 @@ static void *pgsql_out_stop_one(void *arg) } } } + time(&end_command); + fprintf(stderr, "Creating hstore indexes on %s. Took %li seconds\n", table->name, (end_command - start_command)); } fprintf(stderr, "Creating indexes on %s finished\n", table->name); pgsql_exec(sql_conn, PGRES_COMMAND_OK, "GRANT SELECT ON %s TO PUBLIC;\n", table->name); @@ -1167,21 +1495,37 @@ static void pgsql_out_stop() * access the data simultanious to process the rest in parallel * as well as see the newly created tables. */ - pgsql_out_commit(); - Options->mid->commit(); + //pgsql_out_commit(); + +#ifdef HAVE_PTHREAD + workers_finish = 1; + pthread_cond_broadcast(&cond_worker_queue_work_available); + for (i=0; inum_threads; i++) { + int ret = pthread_join(worker_threads[i], NULL); + if (ret) { + fprintf(stderr, "pthread_join() returned an error (%d)", ret); + exit_nicely(); + } + } + free(worker_threads); + worker_threads = NULL; +#endif + + + Options->mid->commit(global_ctx.middle_ctx); /* To prevent deadlocks in parallel processing, the mid tables need * to stay out of a transaction. In this stage output tables are only * written to and not read, so they can be processed as several parallel * independent transactions */ - for (i=0; imid->iterate_ways( pgsql_out_way ); - pgsql_out_commit(); - Options->mid->commit(); + //pgsql_out_commit(); + Options->mid->commit(global_ctx.middle_ctx); /* Processing any remaing to be processed relations */ /* During this stage output tables also need to stay out of @@ -1190,19 +1534,26 @@ static void pgsql_out_stop() */ Options->mid->iterate_relations( pgsql_process_relation ); - tagtransform_shutdown(); + tagtransform_shutdown(global_ctx.tagtransform_ctx); + close_geometry_ctx(global_ctx.geom_ctx); + global_ctx.geom_ctx = NULL; + + #ifdef HAVE_PTHREAD if (Options->parallel_indexing) { for (i=0; imid->stop(); for (i=0; imid->stop(); - for (i=0; imid->stop(); + for (i=0; imid->nodes_set(id, lat, lon, tags); + Options->mid->nodes_set(global_ctx.middle_ctx, id, lat, lon, tags); pgsql_out_node(id, tags, lat, lon); return 0; } -static int pgsql_add_way(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags) -{ - int polygon = 0; - int roads = 0; - /* Check whether the way is: (1) Exportable, (2) Maybe a polygon */ - int filter = tagtransform_filter_way_tags(tags, &polygon, &roads); +static int pgsql_add_way(osmid_t id, osmid_t *nds, int nd_count, struct keyval *tags){ + struct way_info2 * way = malloc(sizeof(struct way_info2)); + int i; - /* If this isn't a polygon then it can not be part of a multipolygon - Hence only polygons are "pending" */ - Options->mid->ways_set(id, nds, nd_count, tags, (!filter && polygon) ? 1 : 0); +#ifdef HAVE_PTHREAD + if (!worker_threads) { + Options->mid->commit(global_ctx.middle_ctx); + pgsql_pause_copy(&global_tables[t_point]); + pgsql_pause_copy(&global_tables[t_line]); + pgsql_pause_copy(&global_tables[t_roads]); + pgsql_pause_copy(&global_tables[t_poly]); + worker_threads = calloc(Options->num_threads, sizeof(pthread_t)); + for (i = 0; i < Options->num_threads; i++) { + int * thread_id = malloc(sizeof(int)); + *thread_id = i + 1; //thread_id is the global_ctx thread. + int ret = pthread_create(&(worker_threads[i]), NULL, + &pgsql_worker_thread, thread_id ); + if (ret) { + fprintf(stderr, "pthread_create() returned an error (%d)", + ret); + exit_nicely(); + } + } + } +#endif //HAVE_PTHREAD - if( !polygon && !filter ) - { - /* Get actual node data and generate output */ - struct osmNode *nodes = malloc( sizeof(struct osmNode) * nd_count ); - int count = Options->mid->nodes_get_list( nodes, nds, nd_count ); - pgsql_out_way(id, tags, nodes, count, 0); - free(nodes); - } - return 0; -} + way->id = id; + way->nd_count = nd_count; + way->nds = malloc(sizeof(osmid_t) * nd_count); + memcpy(way->nds, nds, sizeof(osmid_t) * nd_count); + way->tags = malloc(sizeof(struct keyval)); + initList(way->tags); + cloneList(way->tags, tags); -/* This is the workhorse of pgsql_add_relation, split out because it is used as the callback for iterate relations */ -static int pgsql_process_relation(osmid_t id, struct member *members, int member_count, struct keyval *tags, int exists) -{ - int i, j, count, count2; - osmid_t *xid2 = malloc( (member_count+1) * sizeof(osmid_t) ); - osmid_t *xid; - const char **xrole = malloc( (member_count+1) * sizeof(const char *) ); - int *xcount = malloc( (member_count+1) * sizeof(int) ); - struct keyval *xtags = malloc( (member_count+1) * sizeof(struct keyval) ); - struct osmNode **xnodes = malloc( (member_count+1) * sizeof(struct osmNode*) ); +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&lock_worker_queue); + while ((ways_buffer_pfree + 1) % WORKER_THREAD_QUEUE_SIZE == ways_buffer_pfirst) { + //Queue is full, wait until the worker threads have processed some of it + //and there is space in the queue again. + pthread_cond_wait(&cond_worker_queue_space_available, &lock_worker_queue); + } - /* If the flag says this object may exist already, delete it first */ - if(exists) - pgsql_delete_relation_from_output(id); - - if (tagtransform_filter_rel_tags(tags)) { - free(xid2); - free(xrole); - free(xcount); - free(xtags); - free(xnodes); - return 1; - } + ways_buffer[ways_buffer_pfree] = way; + ways_buffer_pfree++; + if (ways_buffer_pfree > (WORKER_THREAD_QUEUE_SIZE - 1)) + ways_buffer_pfree = 0; + pthread_mutex_unlock(&lock_worker_queue); + pthread_cond_signal(&cond_worker_queue_work_available); + return 0; +#else + pgsql_add_way_single(&global_ctx, way); +#endif - count = 0; - for( i=0; imid->ways_get_list(xid2, count, &xid, xtags, xnodes, xcount); - for (i = 0; i < count2; i++) { - for (j = i; j < member_count; j++) { - if (members[j].id == xid[i]) break; - } - xrole[i] = members[j].role; - } - xnodes[count2] = NULL; - xcount[count2] = 0; - xid[count2] = 0; - xrole[count2] = NULL; - /* At some point we might want to consider storing the retrieved data in the members, rather than as separate arrays */ - pgsql_out_relation(id, tags, count2, xnodes, xtags, xcount, xid, xrole); - for( i=0; iid = id; + rel->tags = malloc(sizeof(struct keyval)); + initList(rel->tags); + cloneList(rel->tags, tags); + rel->exists = exists; + rel->member_count = member_count; + rel->members = malloc(sizeof(struct member) * member_count); + for (i = 0; i < member_count; i++) { + rel->members[i].id = members[i].id; + rel->members[i].type = members[i].type; + rel->members[i].role = strdup(members[i].role); + } + +#ifdef HAVE_PTHREAD + if (workers_finish == 0) { + pthread_mutex_lock(&lock_worker_queue); + while ((rels_buffer_pfree + 1) % WORKER_THREAD_QUEUE_SIZE == rels_buffer_pfirst ) { + pthread_cond_wait(&cond_worker_queue_space_available, &lock_worker_queue); + } + + rels_buffer[rels_buffer_pfree] = rel; + rels_buffer_pfree++; + if (rels_buffer_pfree > (WORKER_THREAD_QUEUE_SIZE - 1)) rels_buffer_pfree = 0; + pthread_mutex_unlock(&lock_worker_queue); + pthread_cond_signal(&cond_worker_queue_work_available); + return 0; + } else +#endif + return pgsql_process_relation_single(&global_ctx, rel); } static int pgsql_add_relation(osmid_t id, struct member *members, int member_count, struct keyval *tags) @@ -1339,7 +1695,7 @@ static int pgsql_add_relation(osmid_t id, struct member *members, int member_cou /* In slim mode we remember these */ if(Options->mid->relations_set) - Options->mid->relations_set(id, members, member_count, tags); + Options->mid->relations_set(global_ctx.middle_ctx, id, members, member_count, tags); /* Only a limited subset of type= is supported, ignore other */ if ( (strcmp(type, "route") != 0) && (strcmp(type, "multipolygon") != 0) && (strcmp(type, "boundary") != 0)) @@ -1360,16 +1716,16 @@ static int pgsql_delete_node(osmid_t osm_id) fprintf( stderr, "Cannot apply diffs unless in slim mode\n" ); exit_nicely(); } - pgsql_pause_copy(&tables[t_point]); - if ( expire_tiles_from_db(tables[t_point].sql_conn, osm_id) != 0) - pgsql_exec(tables[t_point].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_point].name, osm_id ); + pgsql_pause_copy(&global_tables[t_point]); + if ( expire_tiles_from_db(global_tables[t_point].sql_conn, osm_id) != 0) + pgsql_exec(global_tables[t_point].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, global_tables[t_point].name, osm_id ); - Options->mid->nodes_delete(osm_id); + Options->mid->nodes_delete(global_ctx.middle_ctx, osm_id); return 0; } /* Seperated out because we use it elsewhere */ -static int pgsql_delete_way_from_output(osmid_t osm_id) +static int pgsql_delete_way_from_output(osmid_t osm_id, struct s_table * tables) { /* Optimisation: we only need this is slim mode */ if( !Options->slim ) @@ -1377,12 +1733,13 @@ static int pgsql_delete_way_from_output(osmid_t osm_id) /* in droptemp mode we don't have indices and this takes ages. */ if (Options->droptemp) return 0; - pgsql_pause_copy(&tables[t_roads]); - pgsql_pause_copy(&tables[t_line]); - pgsql_pause_copy(&tables[t_poly]); - pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_roads].name, osm_id ); - if ( expire_tiles_from_db(tables[t_line].sql_conn, osm_id) != 0) + pgsql_pause_copy(&(tables[t_roads])); + pgsql_pause_copy(&(tables[t_line])); + pgsql_pause_copy(&(tables[t_poly])); + if ( expire_tiles_from_db(tables[t_line].sql_conn, osm_id) != 0) { pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_line].name, osm_id ); + pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_roads].name, osm_id ); + } if ( expire_tiles_from_db(tables[t_poly].sql_conn, osm_id) != 0) pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_poly].name, osm_id ); return 0; @@ -1395,22 +1752,22 @@ static int pgsql_delete_way(osmid_t osm_id) fprintf( stderr, "Cannot apply diffs unless in slim mode\n" ); exit_nicely(); } - pgsql_delete_way_from_output(osm_id); - Options->mid->ways_delete(osm_id); + pgsql_delete_way_from_output(osm_id, global_tables); + Options->mid->ways_delete(global_ctx.middle_ctx, osm_id); return 0; } /* Relations are identified by using negative IDs */ static int pgsql_delete_relation_from_output(osmid_t osm_id) { - pgsql_pause_copy(&tables[t_roads]); - pgsql_pause_copy(&tables[t_line]); - pgsql_pause_copy(&tables[t_poly]); - pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_roads].name, -osm_id ); - if ( expire_tiles_from_db(tables[t_line].sql_conn, -osm_id) != 0) - pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_line].name, -osm_id ); - if ( expire_tiles_from_db(tables[t_poly].sql_conn, -osm_id) != 0) - pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, tables[t_poly].name, -osm_id ); + pgsql_pause_copy(&global_tables[t_roads]); + pgsql_pause_copy(&global_tables[t_line]); + pgsql_pause_copy(&global_tables[t_poly]); + pgsql_exec(global_tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, global_tables[t_roads].name, -osm_id ); + if ( expire_tiles_from_db(global_tables[t_line].sql_conn, -osm_id) != 0) + pgsql_exec(global_tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, global_tables[t_line].name, -osm_id ); + if ( expire_tiles_from_db(global_tables[t_poly].sql_conn, -osm_id) != 0) + pgsql_exec(global_tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %" PRIdOSMID, global_tables[t_poly].name, -osm_id ); return 0; } @@ -1422,7 +1779,7 @@ static int pgsql_delete_relation(osmid_t osm_id) exit_nicely(); } pgsql_delete_relation_from_output(osm_id); - Options->mid->relations_delete(osm_id); + Options->mid->relations_delete(global_ctx.middle_ctx, osm_id); return 0; } @@ -1438,7 +1795,7 @@ static int pgsql_modify_node(osmid_t osm_id, double lat, double lon, struct keyv } pgsql_delete_node(osm_id); pgsql_add_node(osm_id, lat, lon, tags); - Options->mid->node_changed(osm_id); + Options->mid->node_changed(global_ctx.middle_ctx, osm_id); return 0; } @@ -1451,7 +1808,7 @@ static int pgsql_modify_way(osmid_t osm_id, osmid_t *nodes, int node_count, stru } pgsql_delete_way(osm_id); pgsql_add_way(osm_id, nodes, node_count, tags); - Options->mid->way_changed(osm_id); + Options->mid->way_changed(global_ctx.middle_ctx, osm_id); return 0; } @@ -1464,7 +1821,7 @@ static int pgsql_modify_relation(osmid_t osm_id, struct member *members, int mem } pgsql_delete_relation(osm_id); pgsql_add_relation(osm_id, members, member_count, tags); - Options->mid->relation_changed(osm_id); + Options->mid->relation_changed(global_ctx.middle_ctx, osm_id); return 0; } diff --git a/output-pgsql.h b/output-pgsql.h index 32af34e78..dde274221 100644 --- a/output-pgsql.h +++ b/output-pgsql.h @@ -22,6 +22,40 @@ struct taginfo { int count; }; +struct relation_info2 { + osmid_t id; + struct keyval *tags; + struct member *members; + int member_count; + int exists; +}; + +struct relation_info { + osmid_t id; + struct keyval * tags; + int member_count; + const char ** member_roles; + struct keyval * member_tags; + int * member_way_node_count; + struct osmNode ** member_way_nodes; + osmid_t * member_ids; +}; + +struct way_info { + osmid_t id; + struct keyval * tags; + struct osmNode *nodes; + int node_count; + int exists; +}; + +struct way_info2 { + osmid_t id; + osmid_t *nds; + int nd_count; + struct keyval *tags; +}; + extern struct output_t out_pgsql; #endif diff --git a/output.h b/output.h index 8bfa7d1e5..c8e57786f 100644 --- a/output.h +++ b/output.h @@ -48,7 +48,9 @@ struct output_options { int parallel_indexing; int alloc_chunkwise; int num_procs; + int num_threads; int droptemp; /* drop slim mode temp tables after act */ + int cluster; /* type of clustering of geometries for faster rendering */ int unlogged; /* use unlogged tables where possible */ int hstore_match_only; /* only copy rows that match an explicitly listed key */ int flat_node_cache_enabled; @@ -59,9 +61,9 @@ struct output_options { struct output_t { int (*start)(const struct output_options *options); - int (*connect)(const struct output_options *options, int startTransaction); + int (*connect)(const struct output_options *options, void * middle_ctx, int startTransaction); void (*stop)(); - void (*cleanup)(void); + void (*cleanup)(void *); void (*close)(int stopTransaction); int (*node_add)(osmid_t id, double lat, double lon, struct keyval *tags); diff --git a/parse-pbf.c b/parse-pbf.c index 0d09df012..a726a1d06 100644 --- a/parse-pbf.c +++ b/parse-pbf.c @@ -243,6 +243,7 @@ int addInfoItems(struct keyval *head, Info *info, StringTable *string_table) memcpy(username, user.data, user.len); addItem(head, "osm_user", username, 0); + free(username); } /* TODO timestamp */ @@ -346,6 +347,7 @@ int processOsmDataDenseNodes(struct osmdata_t *osmdata, PrimitiveGroup *group, S valstr = calloc(string_table->s[deltauser_sid].len + 1, 1); memcpy(valstr, string_table->s[deltauser_sid].data, string_table->s[deltauser_sid].len); addItem(&(osmdata->tags), "osm_user", valstr, 0); + free(valstr); } } diff --git a/tagtransform.c b/tagtransform.c index 2814815e7..7607c1fb2 100644 --- a/tagtransform.c +++ b/tagtransform.c @@ -10,9 +10,15 @@ #include "wildcmp.h" #ifdef HAVE_LUA -static lua_State *L; +struct tagtransform_lua_ctx { + lua_State *L; +}; #endif +struct tagtransform_c_ctx { + //empty struct for reference +}; + static struct { int offset; const char *highway; @@ -94,8 +100,10 @@ static int add_z_order(struct keyval *tags, int *roads) { return 0; } -static unsigned int tagtransform_lua_filter_basic_tags(enum OsmType type, struct keyval *tags, int * polygon, int * roads) { +static unsigned int tagtransform_lua_filter_basic_tags(void * ctx_p, enum OsmType type, struct keyval *tags, int * polygon, int * roads) { #ifdef HAVE_LUA + struct tagtransform_lua_ctx * ctx = ctx_p; + struct lua_State * L = ctx->L; int idx = 0; int filter; int count = 0; @@ -292,11 +300,12 @@ static unsigned int tagtransform_c_filter_basic_tags(enum OsmType type, } -static unsigned int tagtransform_lua_filter_rel_member_tags(struct keyval *rel_tags, int member_count, +static unsigned int tagtransform_lua_filter_rel_member_tags(void * ctx_p, struct keyval *rel_tags, int member_count, struct keyval *member_tags,const char **member_role, int * member_superseeded, int * make_boundary, int * make_polygon, int * roads) { #ifdef HAVE_LUA - + struct tagtransform_lua_ctx * ctx = ctx_p; + struct lua_State * L = ctx->L; int i; int idx = 0; int filter; @@ -649,61 +658,74 @@ static unsigned int tagtransform_c_filter_rel_member_tags( resetList(rel_tags); cloneList(rel_tags, &tags); resetList(&tags); + resetList(&poly_tags); add_z_order(rel_tags, roads); return 0; } -static int tagtransform_lua_init() { +static void * tagtransform_lua_init() { #ifdef HAVE_LUA + lua_State *L; + struct tagtransform_lua_ctx * ctx = (struct tagtransform_lua_ctx *)malloc(sizeof(struct tagtransform_lua_ctx)); L = luaL_newstate(); + ctx->L = L; luaL_openlibs(L); luaL_dofile(L, options->tag_transform_script); lua_getglobal(L, "filter_tags_node"); if (!lua_isfunction (L, -1)) { fprintf(stderr,"Tag transform style does not contain a function filter_tags_node\n"); - return 1; + lua_close(L); + free(ctx); + return NULL; } lua_pop(L,1); lua_getglobal(L, "filter_tags_way"); if (!lua_isfunction (L, -1)) { fprintf(stderr,"Tag transform style does not contain a function filter_tags_way\n"); - return 1; + lua_close(L); + free(ctx); + return NULL; } lua_pop(L,1); lua_getglobal(L, "filter_basic_tags_rel"); if (!lua_isfunction (L, -1)) { fprintf(stderr,"Tag transform style does not contain a function filter_basic_tags_rel\n"); - return 1; + lua_close(L); + free(ctx); + return NULL; } lua_getglobal(L, "filter_tags_relation_member"); if (!lua_isfunction (L, -1)) { fprintf(stderr,"Tag transform style does not contain a function filter_tags_relation_member\n"); - return 1; + lua_close(L); + free(ctx); + return NULL; } - return 0; + return ctx; #else fprintf(stderr,"Error: Could not init lua tag transform, as lua support was not compiled into this version\n"); return 1; #endif } -void tagtransform_lua_shutdown() { +void tagtransform_lua_shutdown(void * ctx_p) { #ifdef HAVE_LUA - lua_close(L); + struct tagtransform_lua_ctx * ctx = ctx_p; + lua_close(ctx->L); #endif } -unsigned int tagtransform_filter_node_tags(struct keyval *tags) { +unsigned int tagtransform_filter_node_tags(void * ctx, struct keyval *tags) { int poly, roads; if (transform_method) { - return tagtransform_lua_filter_basic_tags(OSMTYPE_NODE, tags, &poly, &roads); + return tagtransform_lua_filter_basic_tags(ctx, OSMTYPE_NODE, tags, &poly, &roads); } else { return tagtransform_c_filter_basic_tags(OSMTYPE_NODE, tags, &poly, &roads); } @@ -712,32 +734,32 @@ unsigned int tagtransform_filter_node_tags(struct keyval *tags) { /* * This function gets called twice during initial import per way. Once from add_way and once from out_way */ -unsigned int tagtransform_filter_way_tags(struct keyval *tags, int * polygon, int * roads) { +unsigned int tagtransform_filter_way_tags(void * ctx, struct keyval *tags, int * polygon, int * roads) { if (transform_method) { - return tagtransform_lua_filter_basic_tags(OSMTYPE_WAY, tags, polygon, roads); + return tagtransform_lua_filter_basic_tags(ctx, OSMTYPE_WAY, tags, polygon, roads); } else { return tagtransform_c_filter_basic_tags(OSMTYPE_WAY, tags, polygon, roads); } } -unsigned int tagtransform_filter_rel_tags(struct keyval *tags) { +unsigned int tagtransform_filter_rel_tags(void * ctx, struct keyval *tags) { int poly, roads; if (transform_method) { - return tagtransform_lua_filter_basic_tags(OSMTYPE_RELATION, tags, &poly, &roads); + return tagtransform_lua_filter_basic_tags(ctx, OSMTYPE_RELATION, tags, &poly, &roads); } else { return tagtransform_c_filter_basic_tags(OSMTYPE_RELATION, tags, &poly, &roads); } } -unsigned int tagtransform_filter_rel_member_tags(struct keyval *rel_tags, int member_count, struct keyval *member_tags,const char **member_role, int * member_superseeded, int * make_boundary, int * make_polygon, int * roads) { +unsigned int tagtransform_filter_rel_member_tags(void * ctx, struct keyval *rel_tags, int member_count, struct keyval *member_tags,const char **member_role, int * member_superseeded, int * make_boundary, int * make_polygon, int * roads) { if (transform_method) { - return tagtransform_lua_filter_rel_member_tags(rel_tags, member_count, member_tags, member_role, member_superseeded, make_boundary, make_polygon, roads); + return tagtransform_lua_filter_rel_member_tags(ctx, rel_tags, member_count, member_tags, member_role, member_superseeded, make_boundary, make_polygon, roads); } else { return tagtransform_c_filter_rel_member_tags(rel_tags, member_count, member_tags, member_role, member_superseeded, make_boundary, make_polygon, roads); } } -int tagtransform_init(const struct output_options *opts) { +void * tagtransform_init(const struct output_options *opts) { options = opts; if (opts->tag_transform_script) { transform_method = 1; @@ -746,11 +768,13 @@ int tagtransform_init(const struct output_options *opts) { } else { transform_method = 0; fprintf(stderr, "Using built-in tag processing pipeline\n"); - return 0; //Nothing to initialise + return malloc(sizeof(struct tagtransform_c_ctx)); //Nothing to initialise } } -void tagtransform_shutdown() { +void tagtransform_shutdown(void * ctx) { if (transform_method) - tagtransform_lua_shutdown(); + tagtransform_lua_shutdown(ctx); + else + free(ctx); } diff --git a/tagtransform.h b/tagtransform.h index 3fe12526f..6f093d9c1 100644 --- a/tagtransform.h +++ b/tagtransform.h @@ -14,13 +14,13 @@ extern "C" { #endif -unsigned int tagtransform_filter_node_tags(struct keyval *tags); -unsigned int tagtransform_filter_way_tags(struct keyval *tags, int * polygon, int * roads); -unsigned int tagtransform_filter_rel_tags(struct keyval *tags); -unsigned int tagtransform_filter_rel_member_tags(struct keyval *rel_tags, int member_count, struct keyval *member_tags,const char **member_role, int * member_superseeded, int * make_boundary, int * make_polygon, int * roads); +unsigned int tagtransform_filter_node_tags(void * ctx, struct keyval *tags); +unsigned int tagtransform_filter_way_tags(void * ctx, struct keyval *tags, int * polygon, int * roads); +unsigned int tagtransform_filter_rel_tags(void * ctx, struct keyval *tags); +unsigned int tagtransform_filter_rel_member_tags(void * ctx, struct keyval *rel_tags, int member_count, struct keyval *member_tags,const char **member_role, int * member_superseeded, int * make_boundary, int * make_polygon, int * roads); -int tagtransform_init(const struct output_options *options); -void tagtransform_shutdown(); +void * tagtransform_init(const struct output_options *options); +void tagtransform_shutdown(void * ctx); #ifdef __cplusplus } diff --git a/tests/regression-test.py b/tests/regression-test.py index ada11e3ec..5af4ff091 100644 --- a/tests/regression-test.py +++ b/tests/regression-test.py @@ -279,6 +279,10 @@ def __init__(self): [26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42, 43, 44, 47, 48, 62, 63, 64, 65, 68, 69, 72, 73, 74, 78, 79, 82, 83, 84, 86, 87, 88, 106,107,108,109,110,111,112,113,114,115,116,117,118,119], [28,29,30,31,32,33,34,35,36,37,38,39,40,41,42, 43, 44, 47, 48, 62, 63, 64, 65, 66, 67, 70, 71, 75, 76, 79, 80, 81, 83, 84, 85, 87, 89, 90])) + self.addTest(MultipolygonSlimTestCase("basic case - multi-processing", ["--number-processes", "8"], + [26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42, 43, 44, 47, 48, 62, 63, 64, 65, 68, 69, 72, 73, 74, 78, 79, 82, 83, 84, 86, 87, 88, + 106,107,108,109,110,111,112,113,114,115,116,117,118,119], + [28,29,30,31,32,33,34,35,36,37,38,39,40,41,42, 43, 44, 47, 48, 62, 63, 64, 65, 66, 67, 70, 71, 75, 76, 79, 80, 81, 83, 84, 85, 87, 89, 90])) self.addTest(MultipolygonSlimTestCase("multi geometry", ["-G"], [26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42, 43, 45, 46, 47, 49, 50, 62, 63, 64, 65, 68, 69, 72, 73, 74, 78, 79, 82, 83, 84, 86, 87, 88, 106,107,108,109,110,111,112,113,114,115,116,117,118,119], diff --git a/text-tree.c b/text-tree.c index 7670e11f4..38db59b28 100644 --- a/text-tree.c +++ b/text-tree.c @@ -4,14 +4,23 @@ * used by keyvals.c to store the key/value strings */ #define _GNU_SOURCE +#include "config.h" #include #include #include #include #include "text-tree.h" +#ifdef HAVE_PTHREAD +#include +#endif + struct tree_context *tree_ctx = NULL; +#ifdef HAVE_PTHREAD +pthread_spinlock_t lock_text_tree; +#endif + int text_compare(const void *pa, const void *pb, void *rb_param) { struct text_node *a = (struct text_node *)pa; @@ -23,6 +32,10 @@ int text_compare(const void *pa, const void *pb, void *rb_param) struct tree_context *text_init(void) { + pthread_spin_init(&lock_text_tree, PTHREAD_PROCESS_PRIVATE); +#ifdef HAVE_PTHREAD + pthread_spin_lock(&lock_text_tree); +#endif struct tree_context *context; struct rb_table *table = rb_create (text_compare, NULL, NULL); @@ -31,10 +44,14 @@ struct tree_context *text_init(void) assert(context); context->table = table; tree_ctx = context; +#ifdef HAVE_PTHREAD + pthread_spin_unlock(&lock_text_tree); +#endif + return context; } -void text_free(void *pa, void *rb_param) +static void text_free(void *pa, void *rb_param) { struct text_node *a = (struct text_node *)pa; rb_param = NULL; @@ -52,14 +69,23 @@ const char *text_get(struct tree_context *context, const char *text) node->str = strdup(text); assert(node->str); node->ref = 0; +#ifdef HAVE_PTHREAD + pthread_spin_lock(&lock_text_tree); +#endif dupe = rb_insert(context->table, (void *)node); if (dupe) { free(node->str); free(node); dupe->ref++; +#ifdef HAVE_PTHREAD + pthread_spin_unlock(&lock_text_tree); +#endif return dupe->str; } else { node->ref++; +#ifdef HAVE_PTHREAD + pthread_spin_unlock(&lock_text_tree); +#endif return node->str; } } @@ -71,9 +97,16 @@ void text_release(struct tree_context *context, const char *text) find.str = (char *)text; find.ref = 0; +#ifdef HAVE_PTHREAD + pthread_spin_lock(&lock_text_tree); +#endif node = rb_find(context->table, (void *)&find); if (!node) { - fprintf(stderr, "failed to find '%s'\n", text); + fprintf(stderr, "failed to find '%s' trying again\n", text); + node = rb_find(context->table, (void *)&find); + if (node) { //TODO: fixme remove + fprintf(stderr, "not repeatable error\n"); + } fprintf(stderr,"still failed\n"); return; } node->ref--; @@ -82,14 +115,23 @@ void text_release(struct tree_context *context, const char *text) free(node->str); free(node); } +#ifdef HAVE_PTHREAD + pthread_spin_unlock(&lock_text_tree); +#endif } void text_exit(void) { struct tree_context *context = tree_ctx; +#ifdef HAVE_PTHREAD + pthread_spin_lock(&lock_text_tree); +#endif rb_destroy(context->table, text_free); free(context); tree_ctx = NULL; +#ifdef HAVE_PTHREAD + pthread_spin_unlock(&lock_text_tree); +#endif } #if 0 int main(int argc, char **argv)