~ubuntu-branches/ubuntu/vivid/virtualbox-ose/vivid

« back to all changes in this revision

Viewing changes to src/VBox/Runtime/r3/linux/semevent-linux.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2009-10-13 23:06:00 UTC
  • mfrom: (0.3.2 upstream) (0.1.12 sid)
  • Revision ID: james.westby@ubuntu.com-20091013230600-xhu2pwizq0wo63l9
Tags: 3.0.8-dfsg-1ubuntu1
* Merge from debian unstable (LP: #444812), remaining changes:
  - Enable DKMS support on virtualbox host and guest modules (LP: #267097)
    - Drop virtualbox-ose{-guest,}-modules-* package templates
    - Recommend *-source instead of *-modules packages
    - Replace error messages related to missing/mismatched
      kernel module accordingly
  - Autoload kernel module
    - LOAD_VBOXDRV_MODULE=1 in virtualbox-ose.default
  - Disable update action
    - patches/u01-disable-update-action.dpatch
  - Virtualbox should go in Accessories, not in System tools (LP: #288590)
    - virtualbox-ose-qt.files/virtualbox-ose.desktop
  - Add apport hook
    - virtualbox-ose.files/source_virtualbox-ose.py
    - virtualbox-ose.install
  - Add launchpad integration
    - control
    - lpi-bug.xpm
    - patches/u02-lp-integration.dpatch
* Try to remove existing dkms modules before adding the new modules
  (LP: #434503)
  - debian/virtualbox-ose-source.postinst
  - debian/virtualbox-ose-guest-source.postinst
* Don't fail if dkms modules have already been removed
  - debian/virtualbox-ose-source.prerm
  - debian/virtualbox-ose-guest-source.prerm

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
 */
30
30
 
31
31
#include <features.h>
32
 
#if __GLIBC_PREREQ(2,6)
 
32
#if __GLIBC_PREREQ(2,6) && !defined(IPRT_WITH_FUTEX_BASED_SEMS)
33
33
 
34
34
/*
35
35
 * glibc 2.6 fixed a serious bug in the mutex implementation. We wrote this
36
 
 * linux specific event semaphores code in order to work around the bug. As it
37
 
 * turns out, this code seems to have an unresolved issue (#2599), so we'll
38
 
 * fall back on the pthread based implementation if glibc is known to contain
39
 
 * the bug fix.
 
36
 * linux specific event semaphores code in order to work around the bug. We
 
37
 * will fall back on the pthread-based implementation if glibc is known to
 
38
 * contain the bug fix.
40
39
 *
41
40
 * The external refernce to epoll_pwait is a hack which prevents that we link
42
41
 * against glibc < 2.6.
54
53
#include <iprt/alloc.h>
55
54
#include <iprt/asm.h>
56
55
#include <iprt/err.h>
 
56
#include <iprt/time.h>
57
57
#include "internal/magics.h"
58
58
 
59
59
#include <errno.h>
81
81
    /** Magic value. */
82
82
    intptr_t volatile   iMagic;
83
83
    /** The futex state variable.
84
 
     * <0 means signaled.
85
 
     *  0 means not signaled, no waiters.
86
 
     * >0 means not signaled, and the value gives the number of waiters.
87
 
     */
 
84
     * 0 means not signalled.
 
85
       1 means signalled. */
 
86
    uint32_t volatile   fSignalled;
 
87
    /** The number of waiting threads */
88
88
    int32_t volatile    cWaiters;
89
89
};
90
90
 
92
92
/**
93
93
 * Wrapper for the futex syscall.
94
94
 */
95
 
static long sys_futex(int32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
 
95
static long sys_futex(uint32_t volatile *uaddr, int op, int val, struct timespec *utime, int32_t *uaddr2, int val3)
96
96
{
97
97
    errno = 0;
98
98
    long rc = syscall(__NR_futex, uaddr, op, val, utime, uaddr2, val3);
116
116
    {
117
117
        pThis->iMagic = RTSEMEVENT_MAGIC;
118
118
        pThis->cWaiters = 0;
 
119
        pThis->fSignalled = 0;
119
120
        *pEventSem = pThis;
120
121
        return VINF_SUCCESS;
121
122
    }
140
141
    ASMAtomicXchgSize(&pThis->iMagic, RTSEMEVENT_MAGIC | UINT32_C(0x80000000));
141
142
    if (ASMAtomicXchgS32(&pThis->cWaiters, INT32_MIN / 2) > 0)
142
143
    {
143
 
        sys_futex(&pThis->cWaiters, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
 
144
        sys_futex(&pThis->fSignalled, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
144
145
        usleep(1000);
145
146
    }
146
147
 
160
161
    struct RTSEMEVENTINTERNAL *pThis = EventSem;
161
162
    AssertReturn(VALID_PTR(pThis) && pThis->iMagic == RTSEMEVENT_MAGIC,
162
163
                 VERR_INVALID_HANDLE);
163
 
    /*
164
 
     * Try signal it.
165
 
     */
166
 
    for (unsigned i = 0;; i++)
167
 
    {
168
 
        int32_t iCur;
169
 
        if (ASMAtomicCmpXchgExS32(&pThis->cWaiters, -1, 0, &iCur))
170
 
            break; /* nobody is waiting */
171
 
        else if (iCur < 0)
172
 
            break; /* already signaled */
173
 
        else
174
 
        {
175
 
            /* somebody is waiting, try wake up one of them. */
176
 
            long cWoken = sys_futex(&pThis->cWaiters, FUTEX_WAKE, 1, NULL, NULL, 0);
177
 
            if (RT_LIKELY(cWoken == 1))
178
 
            {
179
 
                ASMAtomicDecS32(&pThis->cWaiters);
180
 
                break;
181
 
            }
182
 
            AssertMsg(cWoken == 0, ("%ld\n", cWoken));
183
 
 
184
 
            /*
185
 
             * This path is taken in two situations:
186
 
             *      1) A waiting thread is returning from the sys_futex call with a
187
 
             *         non-zero return value.
188
 
             *      2) There are two threads signaling the event at the
189
 
             *         same time and only one thread waiting.
190
 
             *
191
 
             * At this point we know that nobody is activly waiting on the event but
192
 
             * at the same time, we are racing someone updating the state. The current
193
 
             * strategy is to spin till the thread racing us is done, this is kind of
194
 
             * brain dead and need fixing of course.
195
 
             */
196
 
            if (RT_UNLIKELY(i > 32))
197
 
            {
198
 
                if ((i % 128) == 127)
199
 
                    usleep(1000);
200
 
                else if (!(i % 4))
201
 
                    pthread_yield();
202
 
                else
203
 
                    AssertReleaseMsg(i < 4096, ("iCur=%#x pThis=%p\n", iCur, pThis));
204
 
            }
205
 
        }
206
 
 
207
 
        /* Check the magic to fend off races with RTSemEventDestroy. */
208
 
        if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
209
 
            return VERR_SEM_DESTROYED;
210
 
    }
211
 
    return VINF_SUCCESS;
 
164
 
 
165
    ASMAtomicWriteU32(&pThis->fSignalled, 1);
 
166
    if (ASMAtomicReadS32(&pThis->cWaiters) < 1)
 
167
        return VINF_SUCCESS;
 
168
 
 
169
    /* somebody is waiting, try wake up one of them. */
 
170
    long cWoken = sys_futex(&pThis->fSignalled, FUTEX_WAKE, 1, NULL, NULL, 0);
 
171
    if (RT_LIKELY(cWoken >= 0))
 
172
        return VINF_SUCCESS;
 
173
 
 
174
    if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
 
175
        return VERR_SEM_DESTROYED;
 
176
 
 
177
    return VERR_INVALID_PARAMETER;
212
178
}
213
179
 
214
180
 
224
190
    /*
225
191
     * Quickly check whether it's signaled.
226
192
     */
227
 
    if (ASMAtomicCmpXchgS32(&pThis->cWaiters, 0, -1))
 
193
    if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
228
194
        return VINF_SUCCESS;
229
195
 
230
196
    /*
231
 
     * Convert timeout value.
 
197
     * Convert the timeout value.
232
198
     */
233
199
    struct timespec ts;
234
200
    struct timespec *pTimeout = NULL;
 
201
    uint64_t u64End = 0; /* shut up gcc */
235
202
    if (cMillies != RT_INDEFINITE_WAIT)
236
203
    {
 
204
        if (!cMillies)
 
205
            return VERR_TIMEOUT;
237
206
        ts.tv_sec  = cMillies / 1000;
238
207
        ts.tv_nsec = (cMillies % 1000) * 1000000;
 
208
        u64End = RTTimeSystemNanoTS() + cMillies * 1000000;
239
209
        pTimeout = &ts;
240
210
    }
241
211
 
 
212
    ASMAtomicIncS32(&pThis->cWaiters);
 
213
 
242
214
    /*
243
215
     * The wait loop.
244
216
     */
245
 
    for (unsigned i = 0;; i++)
 
217
    int rc = VINF_SUCCESS;
 
218
    for (;;)
246
219
    {
247
 
        /*
248
 
         * Announce that we're among the waiters.
249
 
         */
250
 
        int32_t iNew = ASMAtomicIncS32(&pThis->cWaiters);
251
 
        if (iNew == 0)
252
 
            return VINF_SUCCESS;
253
 
        if (RT_LIKELY(iNew > 0))
254
 
        {
255
 
            /*
256
 
             * Go to sleep.
257
 
             */
258
 
            long rc = sys_futex(&pThis->cWaiters, FUTEX_WAIT, iNew, pTimeout, NULL, 0);
259
 
            if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
260
 
                return VERR_SEM_DESTROYED;
261
 
 
262
 
            /* Did somebody wake us up from RTSemEventSignal()? */
263
 
            if (rc == 0)
264
 
                return VINF_SUCCESS;
265
 
 
266
 
            /* No, then the kernel woke us up or we failed going to sleep. Adjust the accounting. */
267
 
            iNew = ASMAtomicDecS32(&pThis->cWaiters);
268
 
            Assert(iNew >= 0);
269
 
 
270
 
            /*
271
 
             * Act on the wakup code.
272
 
             */
273
 
            if (rc == -ETIMEDOUT)
274
 
            {
275
 
                Assert(pTimeout);
276
 
                return VERR_TIMEOUT;
277
 
            }
278
 
            if (rc == -EWOULDBLOCK)
279
 
                /* retry with new value. */;
280
 
            else if (rc == -EINTR)
281
 
            {
282
 
                if (!fAutoResume)
283
 
                    return VERR_INTERRUPTED;
284
 
            }
285
 
            else
286
 
            {
287
 
                /* this shouldn't happen! */
288
 
                AssertMsgFailed(("rc=%ld errno=%d\n", rc, errno));
289
 
                return RTErrConvertFromErrno(rc);
 
220
        long lrc = sys_futex(&pThis->fSignalled, FUTEX_WAIT, 0, pTimeout, NULL, 0);
 
221
        if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
 
222
        {
 
223
            rc = VERR_SEM_DESTROYED;
 
224
            break;
 
225
        }
 
226
 
 
227
        if (RT_LIKELY(lrc == 0 || lrc == -EWOULDBLOCK))
 
228
        {
 
229
            /* successful wakeup or fSignalled > 0 in the meantime */
 
230
            if (ASMAtomicCmpXchgU32(&pThis->fSignalled, 0, 1))
 
231
                break;
 
232
        }
 
233
        else if (lrc == -ETIMEDOUT)
 
234
        {
 
235
            rc = VERR_TIMEOUT;
 
236
            break;
 
237
        }
 
238
        else if (lrc == -EINTR)
 
239
        {
 
240
            if (!fAutoResume)
 
241
            {
 
242
                rc = VERR_INTERRUPTED;
 
243
                break;
290
244
            }
291
245
        }
292
246
        else
293
247
        {
294
 
            /* this can't happen. */
295
 
            if (RT_UNLIKELY(pThis->iMagic != RTSEMEVENT_MAGIC))
296
 
                return VERR_SEM_DESTROYED;
297
 
            AssertReleaseMsgFailed(("iNew=%d\n", iNew));
 
248
            /* this shouldn't happen! */
 
249
            AssertMsgFailed(("rc=%ld errno=%d\n", lrc, errno));
 
250
            rc = RTErrConvertFromErrno(lrc);
 
251
            break;
 
252
        }
 
253
        /* adjust the relative timeout */
 
254
        if (pTimeout)
 
255
        {
 
256
            int64_t i64Diff = u64End - RTTimeSystemNanoTS();
 
257
            if (i64Diff < 1000)
 
258
            {
 
259
                rc = VERR_TIMEOUT;
 
260
                break;
 
261
            }
 
262
            ts.tv_sec  = i64Diff / 1000000000;
 
263
            ts.tv_nsec = i64Diff % 1000000000;
298
264
        }
299
265
    }
 
266
 
 
267
    ASMAtomicDecS32(&pThis->cWaiters);
 
268
    return rc;
300
269
}
301
270
 
302
271
 
314
283
    return rtSemEventWait(EventSem, cMillies, false);
315
284
}
316
285
 
317
 
#endif /* glibc < 2.6 */
 
286
#endif /* glibc < 2.6 || IPRT_WITH_FUTEX_BASED_SEMS */
 
287