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

Last change on this file since 28454 was 26252, checked in by jonb, 8 years ago

render_list: attempt to adjust the stats for the QMAX tiles which are likely in the queue

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