int mapcache_lock_or_wait_for_resource(mapcache_context *ctx, mapcache_locker *locker, char *resource, void **lock) { mapcache_lock_result rv = locker->aquire_lock(ctx, locker, resource, lock); if(GC_HAS_ERROR(ctx)) { return MAPCACHE_FAILURE; } if(rv == MAPCACHE_LOCK_AQUIRED) return MAPCACHE_TRUE; else { apr_time_t start_wait = apr_time_now(); rv = MAPCACHE_LOCK_LOCKED; while(rv != MAPCACHE_LOCK_NOENT) { unsigned int waited = apr_time_as_msec(apr_time_now()-start_wait); if(waited > locker->timeout*1000) { mapcache_unlock_resource(ctx,locker,resource, *lock); ctx->log(ctx,MAPCACHE_ERROR,"deleting a possibly stale lock after waiting on it for %g seconds",waited/1000.0); return MAPCACHE_FALSE; } apr_sleep(locker->retry_interval * 1000000); rv = locker->ping_lock(ctx,locker,resource, *lock); } return MAPCACHE_FALSE; } }
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 }
/** * \brief write tile data to disk * * writes the content of mapcache_tile::data to disk. * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked * \returns MAPCACHE_SUCCESS if the tile has been successfully written to disk * \private \memberof mapcache_cache_disk * \sa mapcache_cache::tile_set() */ static void _mapcache_cache_disk_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile) { apr_size_t bytes; apr_file_t *f; apr_status_t ret; char errmsg[120]; char *filename, *hackptr1, *hackptr2=NULL; mapcache_cache_disk *cache = (mapcache_cache_disk*)pcache; const int creation_retry = cache->creation_retry; int retry_count_create_file = 0; #ifdef DEBUG /* all this should be checked at a higher level */ if(!tile->encoded_data && !tile->raw_image) { ctx->set_error(ctx,500,"attempting to write empty tile to disk"); return; } if(!tile->encoded_data && !tile->tileset->format) { ctx->set_error(ctx,500,"received a raw tile image for a tileset with no format"); return; } #endif cache->tile_key(ctx, cache, tile, &filename); GC_CHECK_ERROR(ctx); /* find the location of the last '/' in the string */ hackptr1 = filename; while(*hackptr1) { if(*hackptr1 == '/') hackptr2 = hackptr1; hackptr1++; } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { /* * apr_dir_make_recursive sometimes sends back this error, although it should not. * ignore this one */ if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; } } *hackptr2 = '/'; ret = apr_file_remove(filename,ctx->pool); if(ret != APR_SUCCESS && !APR_STATUS_IS_ENOENT(ret)) { ctx->set_error(ctx, 500, "failed to remove file %s: %s",filename, apr_strerror(ret,errmsg,120)); } #ifdef HAVE_SYMLINK if(cache->symlink_blank) { if(!tile->raw_image) { tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data); GC_CHECK_ERROR(ctx); } if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) { char *blankname; int retry_count_create_symlink = 0; char *blankname_rel = NULL; _mapcache_cache_disk_blank_tile_key(ctx,cache,tile,tile->raw_image->data,&blankname); if(apr_file_open(&f, blankname, APR_FOPEN_READ, APR_OS_DEFAULT, ctx->pool) != APR_SUCCESS) { int isLocked; void *lock; char *blankdirname; if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } /* create the blank file */ blankdirname = apr_psprintf(ctx->pool, "%s/%s/%s/blanks", cache->base_directory, tile->tileset->name, tile->grid_link->grid->name); if(APR_SUCCESS != (ret = apr_dir_make_recursive( blankdirname, APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create directory %s for blank tiles",blankdirname, apr_strerror(ret,errmsg,120)); return; } } /* aquire a lock on the blank file */ isLocked = mapcache_lock_or_wait_for_resource(ctx,ctx->config->locker,blankname, &lock); if(isLocked == MAPCACHE_TRUE) { if((ret = apr_file_open(&f, blankname, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY, APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to create file %s: %s",blankname, apr_strerror(ret,errmsg,120)); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; /* we could not create the file */ } bytes = (apr_size_t)tile->encoded_data->size; ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to write data to file %s (wrote %d of %d bytes): %s",blankname, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120)); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; /* we could not create the file */ } if(bytes != tile->encoded_data->size) { ctx->set_error(ctx, 500, "failed to write image data to %s, wrote %d of %d bytes", blankname, (int)bytes, (int)tile->encoded_data->size); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); return; } apr_file_close(f); mapcache_unlock_resource(ctx,ctx->config->locker,blankname, lock); #ifdef DEBUG ctx->log(ctx,MAPCACHE_DEBUG,"created blank tile %s",blankname); #endif } } else { apr_file_close(f); } /* * compute the relative path between tile and blank tile */ blankname_rel = relative_path(ctx,filename, blankname); GC_CHECK_ERROR(ctx); /* * depending on configuration symlink creation will retry if it fails. * this can happen on nfs mounted network storage. * the solution is to create the containing directory again and retry the symlink creation. */ while(symlink(blankname_rel, filename) != 0) { retry_count_create_symlink++; if(retry_count_create_symlink > creation_retry) { char *error = strerror(errno); ctx->set_error(ctx, 500, "failed to link tile %s to %s: %s",filename, blankname_rel, error); return; /* we could not create the file */ } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create symlink, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } } *hackptr2 = '/'; } #ifdef DEBUG ctx->log(ctx, MAPCACHE_DEBUG, "linked blank tile %s to %s",filename,blankname); #endif return; } } #endif /*HAVE_SYMLINK*/ /* go the normal way: either we haven't configured blank tile detection, or the tile was not blank */ if(!tile->encoded_data) { tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format); GC_CHECK_ERROR(ctx); } /* * depending on configuration file creation will retry if it fails. * this can happen on nfs mounted network storage. * the solution is to create the containing directory again and retry the file creation. */ while((ret = apr_file_open(&f, filename, APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY, APR_OS_DEFAULT, ctx->pool)) != APR_SUCCESS) { retry_count_create_file++; if(retry_count_create_file > creation_retry) { ctx->set_error(ctx, 500, "failed to create file %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } *hackptr2 = '\0'; if(APR_SUCCESS != (ret = apr_dir_make_recursive(filename,APR_OS_DEFAULT,ctx->pool))) { if(!APR_STATUS_IS_EEXIST(ret)) { ctx->set_error(ctx, 500, "failed to create file, can not create directory %s: %s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } } *hackptr2 = '/'; } bytes = (apr_size_t)tile->encoded_data->size; ret = apr_file_write(f,(void*)tile->encoded_data->buf,&bytes); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to write data to file %s (wrote %d of %d bytes): %s",filename, (int)bytes, (int)tile->encoded_data->size, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } if(bytes != tile->encoded_data->size) { ctx->set_error(ctx, 500, "failed to write image data to %s, wrote %d of %d bytes", filename, (int)bytes, (int)tile->encoded_data->size); } ret = apr_file_close(f); if(ret != APR_SUCCESS) { ctx->set_error(ctx, 500, "failed to close file %s:%s",filename, apr_strerror(ret,errmsg,120)); return; /* we could not create the file */ } }