Signal thread stop through a pipe; fixes issues where the statistics thread would...
[cubemap] / stats.cpp
1 #include <fcntl.h>
2 #include <stddef.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <time.h>
7 #include <unistd.h>
8 #include <sys/poll.h>
9 #include <errno.h>
10 #include <vector>
11
12 #include "client.h"
13 #include "serverpool.h"
14 #include "stats.h"
15
16 using namespace std;
17
18 extern ServerPool *servers;
19
20 StatsThread::StatsThread(const std::string &stats_file, int stats_interval)
21         : stats_file(stats_file),
22           stats_interval(stats_interval)
23 {
24 }
25
26 void StatsThread::do_work()
27 {
28         while (!should_stop) {
29                 int fd;
30                 FILE *fp;
31                 time_t now;
32                 vector<ClientStats> client_stats;
33
34                 // Open a new, temporary file.
35                 char *filename = strdup((stats_file + ".new.XXXXXX").c_str());
36                 fd = mkostemp(filename, O_WRONLY);
37                 if (fd == -1) {
38                         perror(filename);
39                         free(filename);
40                         goto sleep;
41                 }
42
43                 fp = fdopen(fd, "w");
44                 if (fp == NULL) {
45                         perror("fdopen");
46                         close(fd);
47                         unlink(filename);
48                         free(filename);
49                         goto sleep;
50                 }
51
52                 now = time(NULL);
53                 client_stats = servers->get_client_stats();
54                 for (size_t i = 0; i < client_stats.size(); ++i) {
55                         fprintf(fp, "%s %s %d %llu %llu %llu\n",
56                                 client_stats[i].remote_addr.c_str(),
57                                 client_stats[i].stream_id.c_str(),
58                                 int(now - client_stats[i].connect_time),
59                                 (long long unsigned)(client_stats[i].bytes_sent),
60                                 (long long unsigned)(client_stats[i].bytes_lost),
61                                 (long long unsigned)(client_stats[i].num_loss_events));
62                 }
63                 if (fclose(fp) == EOF) {
64                         perror("fclose");
65                         unlink(filename);
66                         free(filename);
67                         goto sleep;
68                 }
69                 
70                 if (rename(filename, stats_file.c_str()) == -1) {
71                         perror("rename");
72                         unlink(filename);
73                 }
74
75 sleep:
76                 // Wait until the stop_fd pipe is closed, stats_interval timeout,
77                 // or a spurious signal. (The latter will cause us to write stats
78                 // too often, but that's okay.)
79                 pollfd pfd;
80                 pfd.fd = stop_fd_read;
81                 pfd.events = POLLIN | POLLRDHUP;
82
83                 int nfds = poll(&pfd, 1, stats_interval * 1000);
84                 if (nfds == 0 || (nfds == -1 && errno == EINTR)) {
85                         continue;
86                 }
87                 if (nfds == 1) {
88                         // Should stop.
89                         break;
90                 }
91                 if (nfds == -1) {
92                         perror("poll");
93                         usleep(100000);
94                         continue;
95                 }
96         }
97 }