1/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3#ident "$Id$"
4/*======
5This file is part of PerconaFT.
6
7
8Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9
10 PerconaFT is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License, version 2,
12 as published by the Free Software Foundation.
13
14 PerconaFT is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
21
22----------------------------------------
23
24 PerconaFT is free software: you can redistribute it and/or modify
25 it under the terms of the GNU Affero General Public License, version 3,
26 as published by the Free Software Foundation.
27
28 PerconaFT is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU Affero General Public License for more details.
32
33 You should have received a copy of the GNU Affero General Public License
34 along with PerconaFT. If not, see <http://www.gnu.org/licenses/>.
35======= */
36
37#ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38
39#include <toku_portability.h>
40#include <errno.h>
41#include <string.h>
42
43#include "portability/toku_assert.h"
44#include "util/minicron.h"
45
46toku_instr_key *minicron_p_mutex_key;
47toku_instr_key *minicron_p_condvar_key;
48toku_instr_key *minicron_thread_key;
49
50static void toku_gettime(toku_timespec_t *a) {
51 struct timeval tv;
52 gettimeofday(&tv, 0);
53 a->tv_sec = tv.tv_sec;
54 a->tv_nsec = tv.tv_usec * 1000LL;
55}
56
57
58static int
59timespec_compare (toku_timespec_t *a, toku_timespec_t *b) {
60 if (a->tv_sec > b->tv_sec) return 1;
61 if (a->tv_sec < b->tv_sec) return -1;
62 if (a->tv_nsec > b->tv_nsec) return 1;
63 if (a->tv_nsec < b->tv_nsec) return -1;
64 return 0;
65}
66
67// Implementation notes:
68// When calling do_shutdown or change_period, the mutex is obtained, the variables in the minicron struct are modified, and
69// the condition variable is signalled. Possibly the minicron thread will miss the signal. To avoid this problem, whenever
70// the minicron thread acquires the mutex, it must check to see what the variables say to do (e.g., should it shut down?).
71
72static void*
73minicron_do (void *pv)
74{
75 struct minicron *CAST_FROM_VOIDP(p, pv);
76 toku_mutex_lock(&p->mutex);
77 while (1) {
78 if (p->do_shutdown) {
79 toku_mutex_unlock(&p->mutex);
80 toku_instr_delete_current_thread();
81 return toku_pthread_done(nullptr);
82 }
83 if (p->period_in_ms == 0) {
84 // if we aren't supposed to do it then just do an untimed wait.
85 toku_cond_wait(&p->condvar, &p->mutex);
86 }
87 else if (p->period_in_ms <= 1000) {
88 toku_mutex_unlock(&p->mutex);
89 usleep(p->period_in_ms * 1000);
90 toku_mutex_lock(&p->mutex);
91 }
92 else {
93 // Recompute the wakeup time every time (instead of once per call to f) in case the period changges.
94 toku_timespec_t wakeup_at = p->time_of_last_call_to_f;
95 wakeup_at.tv_sec += (p->period_in_ms/1000);
96 wakeup_at.tv_nsec += (p->period_in_ms % 1000) * 1000000;
97 toku_timespec_t now;
98 toku_gettime(&now);
99 int compare = timespec_compare(&wakeup_at, &now);
100 // if the time to wakeup has yet to come, then we sleep
101 // otherwise, we continue
102 if (compare > 0) {
103 int r = toku_cond_timedwait(&p->condvar, &p->mutex, &wakeup_at);
104 if (r!=0 && r!=ETIMEDOUT) fprintf(stderr, "%s:%d r=%d (%s)", __FILE__, __LINE__, r, strerror(r));
105 assert(r==0 || r==ETIMEDOUT);
106 }
107 }
108 // Now we woke up, and we should figure out what to do
109 if (p->do_shutdown) {
110 toku_mutex_unlock(&p->mutex);
111 toku_instr_delete_current_thread();
112 return toku_pthread_done(nullptr);
113 }
114 if (p->period_in_ms > 1000) {
115 toku_timespec_t now;
116 toku_gettime(&now);
117 toku_timespec_t time_to_call = p->time_of_last_call_to_f;
118 time_to_call.tv_sec += p->period_in_ms/1000;
119 time_to_call.tv_nsec += (p->period_in_ms % 1000) * 1000000;
120 int compare = timespec_compare(&time_to_call, &now);
121 if (compare <= 0) {
122 toku_gettime(&p->time_of_last_call_to_f); // the measured period includes the time to make the call.
123 toku_mutex_unlock(&p->mutex);
124 int r = p->f(p->arg);
125 assert(r==0);
126 toku_mutex_lock(&p->mutex);
127
128 }
129 }
130 else if (p->period_in_ms != 0) {
131 toku_mutex_unlock(&p->mutex);
132 int r = p->f(p->arg);
133 assert(r==0);
134 toku_mutex_lock(&p->mutex);
135 }
136 }
137}
138
139int
140toku_minicron_setup(struct minicron *p, uint32_t period_in_ms, int(*f)(void *), void *arg)
141{
142 p->f = f;
143 p->arg = arg;
144 toku_gettime(&p->time_of_last_call_to_f);
145 // printf("now=%.6f", p->time_of_last_call_to_f.tv_sec +
146 // p->time_of_last_call_to_f.tv_nsec*1e-9);
147 p->period_in_ms = period_in_ms;
148 p->do_shutdown = false;
149 toku_mutex_init(*minicron_p_mutex_key, &p->mutex, nullptr);
150 toku_cond_init(*minicron_p_condvar_key, &p->condvar, nullptr);
151 return toku_pthread_create(
152 *minicron_thread_key, &p->thread, nullptr, minicron_do, p);
153}
154
155void toku_minicron_change_period(struct minicron *p, uint32_t new_period) {
156 toku_mutex_lock(&p->mutex);
157 p->period_in_ms = new_period;
158 toku_cond_signal(&p->condvar);
159 toku_mutex_unlock(&p->mutex);
160}
161
162/* unlocked function for use by engine status which takes no locks */
163uint32_t
164toku_minicron_get_period_in_seconds_unlocked(struct minicron *p)
165{
166 uint32_t retval = p->period_in_ms/1000;
167 return retval;
168}
169
170/* unlocked function for use by engine status which takes no locks */
171uint32_t
172toku_minicron_get_period_in_ms_unlocked(struct minicron *p)
173{
174 uint32_t retval = p->period_in_ms;
175 return retval;
176}
177
178int
179toku_minicron_shutdown(struct minicron *p) {
180 toku_mutex_lock(&p->mutex);
181 assert(!p->do_shutdown);
182 p->do_shutdown = true;
183 //printf("%s:%d signalling\n", __FILE__, __LINE__);
184 toku_cond_signal(&p->condvar);
185 toku_mutex_unlock(&p->mutex);
186 void *returned_value;
187 //printf("%s:%d joining\n", __FILE__, __LINE__);
188 int r = toku_pthread_join(p->thread, &returned_value);
189 if (r!=0) fprintf(stderr, "%s:%d r=%d (%s)\n", __FILE__, __LINE__, r, strerror(r));
190 assert(r==0); assert(returned_value==0);
191 toku_cond_destroy(&p->condvar);
192 toku_mutex_destroy(&p->mutex);
193 //printf("%s:%d shutdowned\n", __FILE__, __LINE__);
194 return 0;
195}
196
197bool
198toku_minicron_has_been_shutdown(struct minicron *p) {
199 return p->do_shutdown;
200}
201