source: subversion/applications/utils/mod_tile/render_list.c @ 28750

Last change on this file since 28750 was 28714, checked in by apmon, 7 years ago

[mod_tile] Add some configure compatibility fixes for solaris

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