aboutsummaryrefslogtreecommitdiff
path: root/writeups/pthread-timedwait/pthread-timedwait.c
diff options
context:
space:
mode:
authorDavid Timber <dxdt@dev.snart.me>2025-01-06 03:46:13 +0100
committerDavid Timber <dxdt@dev.snart.me>2025-01-06 03:46:13 +0100
commit985f4d1f53bd4d028e3f99a5434eeaf1a2276530 (patch)
treedc783900c7772a0a7723f5811699d4e8924f042e /writeups/pthread-timedwait/pthread-timedwait.c
parent4b60d5c2a43c0683154818bfda8b1cfe06a3c861 (diff)
Add writeups/pthread-timedwait
Diffstat (limited to 'writeups/pthread-timedwait/pthread-timedwait.c')
-rw-r--r--writeups/pthread-timedwait/pthread-timedwait.c150
1 files changed, 150 insertions, 0 deletions
diff --git a/writeups/pthread-timedwait/pthread-timedwait.c b/writeups/pthread-timedwait/pthread-timedwait.c
new file mode 100644
index 0000000..9b6e7b5
--- /dev/null
+++ b/writeups/pthread-timedwait/pthread-timedwait.c
@@ -0,0 +1,150 @@
+/**
+ * @file pthread-timedwait.c
+ * @brief Demonstrates the effects on pthread_timedwait upon system wall clock
+ * change
+ */
+#define _POSIX_C_SOURCE 199309L
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <pthread.h>
+
+#define ARG0 "pthread_timedwait"
+
+
+struct {
+ // desired sleep time
+ struct timespec t;
+} opts;
+
+/**
+ * @brief Raise nanosecond fraction part
+ * @details "05" -> 50000000, "123" -> 123000000, "005" -> 5000000
+ * @note used to preserve precision
+ *
+ * @param str fraction part, excluding the leading decimal point
+ * @param len length of \param str
+ * @return long the fraction part raised to nanosecond scale
+ */
+long raise_nsec_frac (const char *str, const size_t len) {
+ char m[] = { '0', '0', '0', '0', '0', '0', '0', '0', '0', 0 };
+ char *p;
+ long ret = -1;
+
+ memcpy(m, str, len > 9 ? 9 : len);
+
+ for (p = m; *p != 0 && *p == '0'; p += 1);
+
+ if (*p == 0 && m[0] == '0') {
+ return 0;
+ }
+ if (sscanf(p, "%ld", &ret) != 1) {
+ return -1;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Parse timespec from string, preserving precision
+ *
+ * @param str string to parse
+ * @param ts_out (optional) pointer to timespec struct
+ * @return true if parsed successfully and \param ts_out is set if non-null
+ * @return false otherwise. errno set to EINVAL if \param str is NULL. errno set
+ * to EBADMSG on format error
+ */
+bool parse_ts (const char *str, struct timespec *ts_out) {
+ long long sec = 0;
+ long nsec = 0;
+ int fr;
+ const char *frac, *ipart;
+
+ if (str == NULL) {
+ errno = EINVAL;
+ return false;
+ }
+
+ if (str[0] == '.' || str[0] == ',') {
+ ipart = NULL;
+ frac = str + 1;
+ }
+ else {
+ ipart = str;
+ frac = strchr(str, '.');
+ if (frac == NULL) {
+ frac = strchr(str, ',');
+ }
+ }
+
+ if (ipart != NULL) {
+ fr = sscanf(ipart, "%lld", &sec);
+ if (fr < 1) {
+ errno = EBADMSG;
+ return false;
+ }
+ }
+ if (frac != NULL) {
+ frac += 1;
+ nsec = raise_nsec_frac(frac, strlen(frac));
+ if (nsec < 0) {
+ errno = EBADMSG;
+ return false;
+ }
+ }
+
+ if (ts_out != NULL) {
+ ts_out->tv_sec = sec;
+ ts_out->tv_nsec = nsec;
+ }
+
+ return true;
+}
+
+bool parse_args (const int argc, const char **argv) {
+ if (argc <= 1) {
+ fprintf(stderr, ARG0" <TIME>\n");
+ return false;
+ }
+
+ if (!parse_ts(argv[1], &opts.t)) {
+ perror(ARG0);
+ return false;
+ }
+
+ return true;
+}
+
+int main (const int argc, const char **argv) {
+ pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+ pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+ struct timespec ts[2];
+ struct timespec tp_deadline;
+
+ if (!parse_args(argc, argv)) {
+ return 1;
+ }
+
+ clock_gettime(CLOCK_REALTIME, &tp_deadline);
+ tp_deadline.tv_nsec += opts.t.tv_nsec;
+ tp_deadline.tv_sec += opts.t.tv_sec + (tp_deadline.tv_nsec / 1000000000);
+ tp_deadline.tv_nsec %= 1000000000;
+
+ clock_gettime(CLOCK_MONOTONIC, ts);
+ pthread_mutex_lock(&mtx);
+ pthread_cond_timedwait(&cond, &mtx, &tp_deadline);
+ clock_gettime(CLOCK_MONOTONIC, ts + 1);
+
+ ts[1].tv_nsec -= ts[0].tv_nsec;
+ ts[1].tv_sec -= ts[0].tv_sec;
+ if (ts[1].tv_nsec < 0) {
+ ts[1].tv_sec -= 1;
+ ts[1].tv_nsec += 1000000000;
+ }
+
+ printf("%lld.%03ld\n", (long long)ts[1].tv_sec, ts[1].tv_nsec / 1000000);
+
+ return 0;
+}