]> sigrok.org Git - libserialport.git/blob - timing.c
windows: Use a fixed worst-case WRITEFILE_MAX_SIZE.
[libserialport.git] / timing.c
1 /*
2  * This file is part of the libserialport project.
3  *
4  * Copyright (C) 2019 Martin Ling <martin-libserialport@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include "libserialport.h"
22 #include "libserialport_internal.h"
23
24 SP_PRIV void time_get(struct time *time)
25 {
26 #ifdef _WIN32
27         LARGE_INTEGER count;
28         QueryPerformanceCounter(&count);
29         time->ticks = count.QuadPart;
30 #elif defined(HAVE_CLOCK_GETTIME)
31         struct timespec ts;
32         if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
33                 clock_gettime(CLOCK_REALTIME, &ts);
34         time->tv.tv_sec = ts.tv_sec;
35         time->tv.tv_usec = ts.tv_nsec / 1000;
36 #elif defined(__APPLE__)
37         mach_timebase_info_data_t info;
38         mach_timebase_info(&info);
39         uint64_t ticks = mach_absolute_time();
40         uint64_t ns = (ticks * info.numer) / info.denom;
41         time->tv.tv_sec = ns / 1000000000;
42         time->tv.tv_usec = (ns % 1000000000) / 1000;
43 #else
44         gettimeofday(&time->tv, NULL);
45 #endif
46 }
47
48 SP_PRIV void time_set_ms(struct time *time, unsigned int ms)
49 {
50 #ifdef _WIN32
51         LARGE_INTEGER frequency;
52         QueryPerformanceFrequency(&frequency);
53         time->ticks = ms * (frequency.QuadPart / 1000);
54 #else
55         time->tv.tv_sec = ms / 1000;
56         time->tv.tv_usec = (ms % 1000) * 1000;
57 #endif
58 }
59
60 SP_PRIV void time_add(const struct time *a,
61                 const struct time *b, struct time *result)
62 {
63 #ifdef _WIN32
64         result->ticks = a->ticks + b->ticks;
65 #else
66         timeradd(&a->tv, &b->tv, &result->tv);
67 #endif
68 }
69
70 SP_PRIV void time_sub(const struct time *a,
71                 const struct time *b, struct time *result)
72 {
73 #ifdef _WIN32
74         result->ticks = a->ticks - b->ticks;
75 #else
76         timersub(&a->tv, &b->tv, &result->tv);
77 #endif
78 }
79
80 SP_PRIV bool time_greater(const struct time *a, const struct time *b)
81 {
82 #ifdef _WIN32
83         return (a->ticks > b->ticks);
84 #else
85         return timercmp(&a->tv, &b->tv, >);
86 #endif
87 }
88
89 SP_PRIV void time_as_timeval(const struct time *time, struct timeval *tv)
90 {
91 #ifdef _WIN32
92         LARGE_INTEGER frequency;
93         QueryPerformanceFrequency(&frequency);
94         tv->tv_sec = time->ticks / frequency.QuadPart;
95         tv->tv_usec = (time->ticks % frequency.QuadPart) /
96                 (frequency.QuadPart / 1000000);
97 #else
98         *tv = time->tv;
99 #endif
100 }
101
102 SP_PRIV unsigned int time_as_ms(const struct time *time)
103 {
104 #ifdef _WIN32
105         LARGE_INTEGER frequency;
106         QueryPerformanceFrequency(&frequency);
107         return time->ticks / (frequency.QuadPart / 1000);
108 #else
109         return time->tv.tv_sec * 1000 + time->tv.tv_usec / 1000;
110 #endif
111 }
112
113 SP_PRIV void timeout_start(struct timeout *timeout, unsigned int timeout_ms)
114 {
115         timeout->ms = timeout_ms;
116
117         /* Get time at start of operation. */
118         time_get(&timeout->start);
119         /* Define duration of timeout. */
120         time_set_ms(&timeout->delta, timeout_ms);
121         /* Calculate time at which we should give up. */
122         time_add(&timeout->start, &timeout->delta, &timeout->end);
123         /* Disable limit unless timeout_limit() called. */
124         timeout->limit_ms = 0;
125         /* First blocking call has not yet been made. */
126         timeout->calls_started = false;
127 }
128
129 SP_PRIV void timeout_limit(struct timeout *timeout, unsigned int limit_ms)
130 {
131         timeout->limit_ms = limit_ms;
132         timeout->overflow = (timeout->ms > timeout->limit_ms);
133         time_set_ms(&timeout->delta_max, timeout->limit_ms);
134 }
135
136 SP_PRIV bool timeout_check(struct timeout *timeout)
137 {
138         if (!timeout->calls_started)
139                 return false;
140
141         if (timeout->ms == 0)
142                 return false;
143
144         time_get(&timeout->now);
145         time_sub(&timeout->end, &timeout->now, &timeout->delta);
146         if (timeout->limit_ms)
147                 if ((timeout->overflow = time_greater(&timeout->delta, &timeout->delta_max)))
148                         timeout->delta = timeout->delta_max;
149
150         return time_greater(&timeout->now, &timeout->end);
151 }
152
153 SP_PRIV void timeout_update(struct timeout *timeout)
154 {
155         timeout->calls_started = true;
156 }
157
158 #ifndef _WIN32
159 SP_PRIV struct timeval *timeout_timeval(struct timeout *timeout)
160 {
161         if (timeout->ms == 0)
162                 return NULL;
163
164         time_as_timeval(&timeout->delta, &timeout->delta_tv);
165
166         return &timeout->delta_tv;
167 }
168 #endif
169
170 SP_PRIV unsigned int timeout_remaining_ms(struct timeout *timeout)
171 {
172         if (timeout->limit_ms && timeout->overflow)
173                 return timeout->limit_ms;
174         else
175                 return time_as_ms(&timeout->delta);
176 }