source: subversion/applications/utils/mod_tile/render_old.c @ 25781

Last change on this file since 25781 was 23108, checked in by saerdnaer, 9 years ago

mod_tile: replace all static max_zoom values of 18 with MAX_ZOOM constant. Patch derived from the work of the Geopard Team, especially Jonathan-David SCHRODER <jonathan.schroder@…>

File size: 13.0 KB
Line 
1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <sys/stat.h>
7#include <sys/time.h>
8#include <sys/un.h>
9#include <poll.h>
10#include <errno.h>
11#include <math.h>
12#include <getopt.h>
13#include <time.h>
14#include <sys/types.h>
15#include <dirent.h>
16#include <limits.h>
17#include <string.h>
18
19#include <pthread.h>
20
21
22#include "gen_tile.h"
23#include "protocol.h"
24#include "render_config.h"
25#include "dir_utils.h"
26
27#ifndef METATILE
28#warning("render_old not implemented for non-metatile mode. Feel free to submit fix")
29int main(int argc, char **argv)
30{
31    fprintf(stderr, "render_old not implemented for non-metatile mode. Feel free to submit fix!\n");
32    return -1;
33}
34#else
35
36#define INILINE_MAX 256
37static int minZoom = 0;
38static int maxZoom = MAX_ZOOM;
39static int verbose = 0;
40static int num_render = 0, num_all = 0;
41static time_t planetTime;
42static struct timeval start, end;
43
44
45int work_complete;
46
47void display_rate(struct timeval start, struct timeval end, int num) 
48{
49    int d_s, d_us;
50    float sec;
51
52    d_s  = end.tv_sec  - start.tv_sec;
53    d_us = end.tv_usec - start.tv_usec;
54
55    sec = d_s + d_us / 1000000.0;
56
57    printf("Rendered %d tiles in %.2f seconds (%.2f tiles/s)\n", num, sec, num / sec);
58    fflush(NULL);
59}
60
61static time_t getPlanetTime(char *tile_dir)
62{
63    static time_t last_check;
64    static time_t planet_timestamp;
65    time_t now = time(NULL);
66    struct stat buf;
67    char filename[PATH_MAX];
68
69    snprintf(filename, PATH_MAX-1, "%s/%s", tile_dir, PLANET_TIMESTAMP);
70
71    // Only check for updates periodically
72    if (now < last_check + 300)
73        return planet_timestamp;
74
75    last_check = now;
76    if (stat(filename, &buf)) {
77        fprintf(stderr, "Planet timestamp file (%s) is missing\n", filename);
78        // Make something up
79        planet_timestamp = now - 3 * 24 * 60 * 60;
80    } else {
81        if (buf.st_mtime != planet_timestamp) {
82            printf("Planet file updated at %s", ctime(&buf.st_mtime));
83            planet_timestamp = buf.st_mtime;
84        }
85    }
86    return planet_timestamp;
87}
88
89int get_load_avg(void)
90{
91    FILE *loadavg = fopen("/proc/loadavg", "r");
92    int avg = 1000;
93
94    if (!loadavg) {
95        fprintf(stderr, "failed to read /proc/loadavg");
96        return 1000;
97    }
98    if (fscanf(loadavg, "%d", &avg) != 1) {
99        fprintf(stderr, "failed to parse /proc/loadavg");
100        fclose(loadavg);
101        return 1000;
102    }
103    fclose(loadavg);
104
105    return avg;
106}
107
108
109int process_loop(int fd, char * xmlname, int x, int y, int z)
110{
111    struct protocol cmd, rsp;
112    //struct pollfd fds[1];
113    int ret = 0;
114
115    bzero(&cmd, sizeof(cmd));
116
117    cmd.ver = 2;
118    cmd.cmd = cmdRenderBulk;
119    cmd.z = z;
120    cmd.x = x;
121    cmd.y = y;
122    strcpy(cmd.xmlname, xmlname);
123    //strcpy(cmd.path, "/tmp/foo.png");
124    //printf("Sending request\n");
125    ret = send(fd, &cmd, sizeof(cmd), 0);
126    if (ret != sizeof(cmd)) {
127        perror("send error");
128    }
129        //printf("Waiting for response\n");
130    bzero(&rsp, sizeof(rsp));
131    ret = recv(fd, &rsp, sizeof(rsp), 0);
132    if (ret != sizeof(rsp)) {
133        perror("recv error");
134        return 0;
135    }
136        //printf("Got response\n");
137
138    if (rsp.cmd != cmdDone) {
139        printf("rendering failed, pausing\n");
140        sleep(10);
141    }
142
143    if (!ret)
144        perror("Socket send error");
145    return ret;
146}
147
148void process(int fd, const char *name)
149{
150    char xmlconfig[XMLCONFIG_MAX];
151    int x, y, z;
152
153    if (path_to_xyz(name, xmlconfig, &x, &y, &z))
154        return;
155
156    printf("Requesting xml(%s) x(%d) y(%d) z(%d)\n", xmlconfig, x, y, z);
157    process_loop(fd, xmlconfig, x, y, z);
158}
159
160static void check_load(void)
161{
162    int avg = get_load_avg();
163
164    while (avg >= MAX_LOAD_OLD) {
165        printf("Load average %d, sleeping\n", avg);
166        sleep(5);
167        avg = get_load_avg();
168    }
169}
170
171#define QMAX 32
172
173pthread_mutex_t qLock;
174pthread_cond_t qCondNotEmpty;
175pthread_cond_t qCondNotFull;
176
177unsigned int qLen;
178struct qItem {
179    char *path;
180    struct qItem *next;
181};
182
183struct qItem *qHead, *qTail;
184
185char *fetch(void)
186{
187    // Fetch path to render from queue or NULL on work completion
188    // Must free() pointer after use
189    char *path;
190
191    pthread_mutex_lock(&qLock);
192
193    while (qLen == 0) {
194        if (work_complete) {
195            pthread_mutex_unlock(&qLock);
196            return NULL;
197        }
198        pthread_cond_wait(&qCondNotEmpty, &qLock);
199    }
200
201    // Fetch item from queue
202    if (!qHead) {
203        fprintf(stderr, "Queue failure, null qHead with %d items in list\n", qLen);
204        exit(1);
205    }
206    path = qHead->path;
207
208    if (qHead == qTail) {
209        free(qHead);
210        qHead = NULL;
211        qTail = NULL;
212        qLen = 0;
213    } else {
214        struct qItem *e = qHead;
215        qHead = qHead->next;
216        free(e);
217        if (qLen == QMAX)
218            pthread_cond_signal(&qCondNotFull);
219        qLen--;
220    }
221
222    pthread_mutex_unlock(&qLock);
223    return path;
224}
225
226void enqueue(const char *path)
227{
228    // Add this path in the local render queue
229    struct qItem *e = malloc(sizeof(struct qItem));
230
231    e->path = strdup(path);
232    e->next = NULL;
233
234    if (!e->path) {
235        fprintf(stderr, "Malloc failure\n");
236        exit(1);
237    }
238
239    pthread_mutex_lock(&qLock);
240
241    while (qLen == QMAX) {
242        pthread_cond_wait(&qCondNotFull, &qLock);
243    }
244
245    // Append item to end of queue
246    if (qTail)
247        qTail->next = e;
248    else
249        qHead = e;
250    qTail = e;
251    pthread_cond_signal(&qCondNotEmpty);
252    qLen++;
253
254    pthread_mutex_unlock(&qLock);
255}
256
257static void descend(const char *search)
258{
259    DIR *tiles = opendir(search);
260    struct dirent *entry;
261    char path[PATH_MAX];
262
263    if (!tiles) {
264        fprintf(stderr, "Unable to open directory: %s\n", search);
265        return;
266    }
267
268    while ((entry = readdir(tiles))) {
269        struct stat b;
270        char *p;
271
272        check_load();
273
274        if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
275            continue;
276        snprintf(path, sizeof(path), "%s/%s", search, entry->d_name);
277        if (stat(path, &b))
278            continue;
279        if (S_ISDIR(b.st_mode)) {
280            descend(path);
281            continue;
282        }
283        p = strrchr(path, '.');
284        if (p && !strcmp(p, ".meta")) {
285            num_all++;
286            if (planetTime > b.st_mtime) {
287                // request rendering of  old tile
288                enqueue(path);
289                num_render++;
290                if (!(num_render % 10)) {
291                    gettimeofday(&end, NULL);
292                    printf("\n");
293                    printf("Meta tiles rendered: ");
294                    display_rate(start, end, num_render);
295                    printf("Total tiles rendered: ");
296                    display_rate(start, end, num_render * METATILE * METATILE);
297                    printf("Total tiles handled from input: ");
298                    display_rate(start, end, num_all);
299                }
300            }
301        }
302    }
303    closedir(tiles);
304}
305
306void *thread_main(void *arg)
307{
308    const char *spath = (const char *)arg;
309    int fd;
310    struct sockaddr_un addr;
311    char *tile;
312
313    fd = socket(PF_UNIX, SOCK_STREAM, 0);
314    if (fd < 0) {
315        fprintf(stderr, "failed to create unix socket\n");
316        exit(2);
317    }
318
319    bzero(&addr, sizeof(addr));
320    addr.sun_family = AF_UNIX;
321    strncpy(addr.sun_path, spath, sizeof(addr.sun_path));
322
323    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
324        fprintf(stderr, "socket connect failed for: %s\n", spath);
325        close(fd);
326        return NULL;
327    }
328
329    while((tile = fetch())) {
330        process(fd, tile);
331        free(tile);
332    }
333
334    close(fd);
335
336    return NULL;
337}
338
339void render_layer(const char *name)
340{
341    int z;
342
343    for (z=minZoom; z<=maxZoom; z++) {
344        char path[PATH_MAX];
345        snprintf(path, PATH_MAX, HASH_PATH "/%s/%d", name, z);
346        descend(path);
347    }
348}
349
350pthread_t *workers;
351
352void spawn_workers(int num, const char *spath)
353{
354    int i;
355
356    // Setup request queue
357    pthread_mutex_init(&qLock, NULL);
358    pthread_cond_init(&qCondNotEmpty, NULL);
359    pthread_cond_init(&qCondNotFull, NULL);
360
361    printf("Starting %d rendering threads\n", num);
362    workers = calloc(sizeof(pthread_t), num);
363    if (!workers) {
364        perror("Error allocating worker memory");
365        exit(1);
366    }
367    for(i=0; i<num; i++) {
368        if (pthread_create(&workers[i], NULL, thread_main, (void *)spath)) {
369            perror("Thread creation failed");
370            exit(1);
371        }
372    }
373}
374
375void finish_workers(int num)
376{
377    int i;
378
379    printf("Waiting for rendering threads to finish\n");
380    pthread_mutex_lock(&qLock);
381    work_complete = 1;
382    pthread_mutex_unlock(&qLock);
383    pthread_cond_broadcast(&qCondNotEmpty);
384
385    for(i=0; i<num; i++)
386        pthread_join(workers[i], NULL);
387    free(workers);
388    workers = NULL;
389}
390
391
392int main(int argc, char **argv)
393{
394    char spath[PATH_MAX] = RENDER_SOCKET;
395    char *tile_dir = HASH_PATH;
396    int c;
397    int numThreads = 1;
398
399    while (1) {
400        int option_index = 0;
401        static struct option long_options[] = {
402            {"min-zoom", 1, 0, 'z'},
403            {"max-zoom", 1, 0, 'Z'},
404            {"socket", 1, 0, 's'},
405            {"num-threads", 1, 0, 'n'},
406            {"tile-dir", 1, 0, 't'},
407            {"verbose", 0, 0, 'v'},
408            {"help", 0, 0, 'h'},
409            {0, 0, 0, 0}
410        };
411
412        c = getopt_long(argc, argv, "hvz:Z:s:t:n:", long_options, &option_index);
413        if (c == -1)
414            break;
415
416        switch (c) {
417            case 's':   /* -s, --socket */
418                strncpy(spath, optarg, PATH_MAX-1);
419                spath[PATH_MAX-1] = 0;
420                break;
421            case 't':   /* -t, --tile-dir */
422                tile_dir=strdup(optarg);
423                break;
424            case 'n':   /* -n, --num-threads */
425                numThreads=atoi(optarg);
426                if (numThreads <= 0) {
427                    fprintf(stderr, "Invalid number of threads, must be at least 1\n");
428                    return 1;
429                }
430                break;
431           case 'z':   /* -z, --min-zoom */
432                        minZoom=atoi(optarg);
433                if (minZoom < 0 || minZoom > MAX_ZOOM) {
434                    fprintf(stderr, "Invalid minimum zoom selected, must be between 0 and %d\n", MAX_ZOOM);
435                    return 1;
436                }
437                break;
438            case 'Z':   /* -Z, --max-zoom */
439                maxZoom=atoi(optarg);
440                if (maxZoom < 0 || maxZoom > MAX_ZOOM) { 
441                    fprintf(stderr, "Invalid maximum zoom selected, must be between 0 and %d\n", MAX_ZOOM);
442                    return 1;
443                }
444                break;
445            case 'v':   /* -v, --verbose */
446                verbose=1;
447                break;
448            case 'h':   /* -h, --help */
449                fprintf(stderr, "Usage: render_old [OPTION] ...\n");
450                fprintf(stderr, "Search the rendered tiles and re-render tiles which are older then the last planet import\n");
451                fprintf(stderr, "  -n, --num-threads=N  the number of parallel request threads (default 1)\n");
452                fprintf(stderr, "  -t, --tile-dir       tile cache directory (defaults to '" HASH_PATH "')\n");
453                fprintf(stderr, "  -z, --min-zoom=ZOOM  filter input to only render tiles greater or equal to this zoom level (default 0)\n");
454                fprintf(stderr, "  -Z, --max-zoom=ZOOM  filter input to only render tiles less than or equal to this zoom level (default %d)\n", MAX_ZOOM);
455                fprintf(stderr, "  -s, --socket=SOCKET  unix domain socket name for contacting renderd\n");
456                return -1;
457            default:
458                fprintf(stderr, "unhandled char '%c'\n", c);
459                break;
460        }
461    }
462
463    if (maxZoom < minZoom) {
464        fprintf(stderr, "Invalid zoom range, max zoom must be greater or equal to minimum zoom\n");
465        return 1;
466    }
467
468    fprintf(stderr, "Rendering old tiles\n");
469
470    planetTime = getPlanetTime(tile_dir);
471
472    gettimeofday(&start, NULL);
473
474    FILE * hini ;
475    char line[INILINE_MAX];
476    char value[INILINE_MAX];
477
478    // Load the config
479    if ((hini=fopen(RENDERD_CONFIG, "r"))==NULL) {
480        fprintf(stderr, "Config: cannot open %s\n", RENDERD_CONFIG);
481        exit(7);
482    }
483
484    spawn_workers(numThreads, spath);
485
486    while (fgets(line, INILINE_MAX, hini)!=NULL) {
487        if (line[0] == '[') {
488            if (strlen(line) >= XMLCONFIG_MAX){
489                fprintf(stderr, "XML name too long: %s\n", line);
490                exit(7);
491            }
492            sscanf(line, "[%[^]]", value);
493            // Skip mapnik & renderd sections which are config, not tile layers
494            if (strcmp(value,"mapnik") && strncmp(value, "renderd", 7))
495                render_layer(value);
496        }
497    }
498    fclose(hini);
499
500    finish_workers(numThreads);
501
502    gettimeofday(&end, NULL);
503    printf("\nTotal for all tiles rendered\n");
504    printf("Meta tiles rendered: ");
505    display_rate(start, end, num_render);
506    printf("Total tiles rendered: ");
507    display_rate(start, end, num_render * METATILE * METATILE);
508    printf("Total tiles handled: ");
509    display_rate(start, end, num_all);
510
511    return 0;
512}
513#endif
Note: See TracBrowser for help on using the repository browser.