static void* APR_THREAD_FUNC _thread_get_tile(apr_thread_t *thread, void *data) { _thread_tile* t = (_thread_tile*)data; mapcache_tileset_tile_get(t->ctx, t->tile); #if !USE_THREADPOOL apr_thread_exit(thread, APR_SUCCESS); #endif return NULL; }
static void* APR_THREAD_FUNC seed_thread(apr_thread_t *thread, void *data) #endif { mapcache_tile *tile; mapcache_context seed_ctx = ctx; seed_ctx.log = seed_log; apr_pool_create(&seed_ctx.pool,ctx.pool); tile = mapcache_tileset_tile_create(ctx.pool, tileset, grid_link); tile->dimensions = dimensions; while(1) { struct seed_cmd cmd; apr_status_t ret; apr_pool_clear(seed_ctx.pool); ret = pop_queue(&cmd); if(ret != APR_SUCCESS || cmd.command == MAPCACHE_CMD_STOP) break; tile->x = cmd.x; tile->y = cmd.y; tile->z = cmd.z; if(cmd.command == MAPCACHE_CMD_SEED) { /* aquire a lock on the metatile ?*/ mapcache_metatile *mt = mapcache_tileset_metatile_get(&seed_ctx, tile); int isLocked = mapcache_lock_or_wait_for_resource(&seed_ctx, mapcache_tileset_metatile_resource_key(&seed_ctx,mt)); if(isLocked == MAPCACHE_TRUE) { /* this will query the source to create the tiles, and save them to the cache */ mapcache_tileset_render_metatile(&seed_ctx, mt); mapcache_unlock_resource(&seed_ctx, mapcache_tileset_metatile_resource_key(&seed_ctx,mt)); } } else if (cmd.command == MAPCACHE_CMD_TRANSFER) { int i; mapcache_metatile *mt = mapcache_tileset_metatile_get(&seed_ctx, tile); for (i = 0; i < mt->ntiles; i++) { mapcache_tile *subtile = &mt->tiles[i]; mapcache_tileset_tile_get(&seed_ctx, subtile); subtile->tileset = tileset_transfer; tileset_transfer->cache->tile_set(&seed_ctx, subtile); } } else { //CMD_DELETE mapcache_tileset_tile_delete(&seed_ctx,tile,MAPCACHE_TRUE); } if(seed_ctx.get_error(&seed_ctx)) { error_detected++; ctx.log(&ctx,MAPCACHE_INFO,seed_ctx.get_error_message(&seed_ctx)); } } #ifdef USE_FORK return 0; #else apr_thread_exit(thread,MAPCACHE_SUCCESS); return NULL; #endif }
void mapcache_prefetch_tiles(mapcache_context *ctx, mapcache_tile **tiles, int ntiles) { apr_thread_t **threads; apr_threadattr_t *thread_attrs; int nthreads; #if !APR_HAS_THREADS int i; for(i=0; i<ntiles; i++) { mapcache_tileset_tile_get(ctx, tiles[i]); GC_CHECK_ERROR(ctx); } #else int i,rv; _thread_tile* thread_tiles; if(ntiles==1 || ctx->config->threaded_fetching == 0) { /* if threads disabled, or only fetching a single tile, don't launch a thread for the operation */ for(i=0; i<ntiles; i++) { mapcache_tileset_tile_get(ctx, tiles[i]); GC_CHECK_ERROR(ctx); } return; } /* allocate a thread struct for each tile. Not all will be used */ thread_tiles = (_thread_tile*)apr_pcalloc(ctx->pool,ntiles*sizeof(_thread_tile)); #if 1 || !USE_THREADPOOL /* use multiple threads, to fetch from multiple metatiles and/or multiple tilesets */ apr_threadattr_create(&thread_attrs, ctx->pool); threads = (apr_thread_t**)apr_pcalloc(ctx->pool, ntiles*sizeof(apr_thread_t*)); nthreads = 0; for(i=0; i<ntiles; i++) { int j; thread_tiles[i].tile = tiles[i]; thread_tiles[i].launch = 1; j=i-1; /* * we only launch one thread per metatile as in the unseeded case the threads * for a same metatile will lock while only a single thread launches the actual * rendering request */ while(j>=0) { /* check that the given metatile hasn't been rendered yet */ if(thread_tiles[j].launch && (thread_tiles[i].tile->tileset == thread_tiles[j].tile->tileset) && (thread_tiles[i].tile->x / thread_tiles[i].tile->tileset->metasize_x == thread_tiles[j].tile->x / thread_tiles[j].tile->tileset->metasize_x)&& (thread_tiles[i].tile->y / thread_tiles[i].tile->tileset->metasize_y == thread_tiles[j].tile->y / thread_tiles[j].tile->tileset->metasize_y)) { thread_tiles[i].launch = 0; /* this tile will not have a thread spawned for it */ break; } j--; } if(thread_tiles[i].launch) thread_tiles[i].ctx = ctx->clone(ctx); } for(i=0; i<ntiles; i++) { if(!thread_tiles[i].launch) continue; /* skip tiles that have been marked */ rv = apr_thread_create(&threads[i], thread_attrs, _thread_get_tile, (void*)&(thread_tiles[i]), thread_tiles[i].ctx->pool); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500, "failed to create thread %d of %d\n",i,ntiles); break; } nthreads++; } /* wait for launched threads to finish */ for(i=0; i<ntiles; i++) { if(!thread_tiles[i].launch) continue; apr_thread_join(&rv, threads[i]); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500, "thread %d of %d failed on exit\n",i,ntiles); } if(GC_HAS_ERROR(thread_tiles[i].ctx)) { /* transfer error message from child thread to main context */ ctx->set_error(ctx,thread_tiles[i].ctx->get_error(thread_tiles[i].ctx), thread_tiles[i].ctx->get_error_message(thread_tiles[i].ctx)); } } for(i=0; i<ntiles; i++) { /* fetch the tiles that did not get a thread launched for them */ if(thread_tiles[i].launch) continue; mapcache_tileset_tile_get(ctx, tiles[i]); GC_CHECK_ERROR(ctx); } #else /* experimental version using a threadpool, disabled for stability reasons */ apr_thread_pool_t *thread_pool; apr_thread_pool_create(&thread_pool,2,ctx->config->download_threads,ctx->pool); for(i=0; i<ntiles; i++) { ctx->log(ctx,MAPCACHE_DEBUG,"starting thread for tile %s",tiles[i]->tileset->name); thread_tiles[i].tile = tiles[i]; thread_tiles[i].ctx = ctx->clone(ctx); rv = apr_thread_pool_push(thread_pool,_thread_get_tile,(void*)&(thread_tiles[i]), 0,NULL); if(rv != APR_SUCCESS) { ctx->set_error(ctx,500, "failed to push thread %d of %d in thread pool\n",i,ntiles); break; } } GC_CHECK_ERROR(ctx); while(apr_thread_pool_tasks_run_count(thread_pool) != ntiles || apr_thread_pool_busy_count(thread_pool)>0) apr_sleep(10000); apr_thread_pool_destroy(thread_pool); for(i=0; i<ntiles; i++) { if(GC_HAS_ERROR(thread_tiles[i].ctx)) { ctx->set_error(ctx,thread_tiles[i].ctx->get_error(thread_tiles[i].ctx), thread_tiles[i].ctx->get_error_message(thread_tiles[i].ctx)); } } #endif #endif }