[patch 1/2] semaphores: Add down_interruptible_timeout()

From: inaky
Date: Mon Feb 26 2007 - 19:12:45 EST


I was in a situation where I could really simplify many things by
using down_interruptible() with a timeout. I went around looking for
how could one be implemented and ran away from the asm code in arch/.

At the end I realized I could make a simple one by setting up a timer
that would 'fake' a sigpending situation (setting the task's TIF_SIGPENDING
bit) so that the __down_interruptible_failed() path would quit when
the timer went off.

I gave it a quick try (must admit, not too tested) and it seems that
the setting of TIF_SIGPENDING without really having a signal queued
is not having easily visible ugly consequences.

If the timeout arrives right after a signal was delivered but before
the thread returns from down_interruptible, then it will also look
like a timeout (as the code in the if statement will kick in) -- to
some extent, it is 'right' theoretically, as it didn't get the sem
before the time expired. TIF_SIGPENDING is still set, so the signal is
not lost (unless I miss something else about the signal delivery
engine).

IF the timeout arrives after the signal and after down_interruptible
returns, no side effects happen. There is a window where the timer
could still execute before it is deleted and it would look like a
timeout [which theoretically could be right too]. Maybe the result <
0 && data.result < 0 check should be done before
del_timer(). Suggestions accepted.

Arjan van de Ven suggested I moved the declaration into
asm-generic/semaphore.h to lay the egg for a saner sharing of
semaphore code between arches. This patch also introduces that witn
the down_interruptible_timeout() decl and the next adds the inclusion
to all the arches' semaphore.h [note: haven't been able to compile
test all of them, just i386].

Signed-off-by: Inaky Perez-Gonzalez <inaky@xxxxxxxxxxxxxxx>

---
include/asm-generic/semaphore.h | 15 ++++++++++
lib/semaphore-sleepers.c | 60 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 75 insertions(+)

Index: linux.hg/lib/semaphore-sleepers.c
===================================================================
--- linux.hg.orig/lib/semaphore-sleepers.c 2007-02-26 14:45:15.000000000 -0800
+++ linux.hg/lib/semaphore-sleepers.c 2007-02-26 14:46:36.000000000 -0800
@@ -174,3 +174,63 @@
spin_unlock_irqrestore(&sem->wait.lock, flags);
return 1;
}
+
+
+struct dit_data
+{
+ struct task_struct *task;
+ int result;
+};
+
+
+/**
+ * dit_kick_process - set a fake 'signal pending' and kick
+ *
+ * Helper for down_interruptible_timeout(). Sets the signal pending
+ * flag on the task that is waiting for a semaphore and kicks it. That
+ * tells the down_interruptible() code to quit waiting.
+ */
+static
+void dit_kick_process(unsigned long _data)
+{
+ struct dit_data *data = (void *) _data;
+ set_tsk_thread_flag(data->task, TIF_SIGPENDING);
+ data->result = -ETIMEDOUT;
+ kick_process(data->task);
+}
+
+
+/*
+ * Interruptible try to acquire a semaphore.
+ *
+ * If we obtained it, return zero. If we were interrupted, returns
+ * -EINTR. If we timedout, we return -ETIMEDOUT;
+ *
+ * Note the ugly hack, using a helper timer and function to wake up
+ * the waiter. We need to distinguish, if we get an error, if it was
+ * because we were woken up (-ETIMEDOUT) or for other reason (-EINTR),
+ * hence the last if block.
+ *
+ * There is some fine print if a signal arrives at a time close to the
+ * timeout, being the most confusing case if the timeout arrives AFTER
+ * the signal but before del_timer() get's called. In that case
+ * down_interruptible_timeout() will return -ETIMEDOUT even when the
+ * signal arrived first. However, the signal pending condition is not
+ * cleared.
+ */
+int down_interruptible_timeout(struct semaphore *sem, unsigned long to)
+{
+ int result;
+ struct dit_data data = {
+ .task = get_current(), .result = 0
+ };
+ DEFINE_TIMER(dit_timer, dit_kick_process, to, (unsigned long) &data);
+ add_timer(&dit_timer);
+ result = down_interruptible(sem);
+ del_timer(&dit_timer);
+ if (result < 0 && data.result < 0)
+ result = data.result;
+ return result;
+}
+EXPORT_SYMBOL_GPL(down_interruptible_timeout);
+
Index: linux.hg/include/asm-generic/semaphore.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux.hg/include/asm-generic/semaphore.h 2007-02-26 14:46:36.000000000 -0800
@@ -0,0 +1,32 @@
+/*
+ * Generic code for semaphores
+ *
+ * Copyright (C) 2007 Inaky Perez-Gonzalez <inaky.perez-gonzalez@xxxxxxxxx>
+ * - down_interruptible_timeout()'s implementation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+#ifndef _GENERIC_SEMAPHORE_H
+#define _GENERIC_SEMAPHORE_H
+
+#ifdef __KERNEL__
+
+struct semaphore;
+extern void down_timeout_interruptible(struct semaphore *);
+
+#endif /* #ifdef __KERNEL__ */
+
+#endif /* #define _GENERIC_SEMAPHORE_H */

--

Inaky
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/