mirror of
https://github.com/gnustep/libs-base.git
synced 2025-05-31 00:30:53 +00:00
lockBeforeDate improvements.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@17263 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
b9bccee8c3
commit
7e4ef0cdc9
4 changed files with 208 additions and 101 deletions
|
@ -1,3 +1,12 @@
|
||||||
|
2003-07-20 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* Source/NSThread.m: Separate out sleeping into a more efficient
|
||||||
|
function and improve use of nanosleep
|
||||||
|
* Source/NSLock.m: Use new private sleeping function from NSThread.m
|
||||||
|
and rewrite code to be more responsive, especially for cases where
|
||||||
|
locks are heavily used for rapid interaction between threads.
|
||||||
|
* Tools/thread.m: Test lockBeforeDate
|
||||||
|
|
||||||
2003-07-17 Richard Frith-Macdonald <rfm@gnu.org>
|
2003-07-17 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/NSRunLoop.m: ([-acceptInputForMode:beforeDate:]) use the
|
* Source/NSRunLoop.m: ([-acceptInputForMode:beforeDate:]) use the
|
||||||
|
|
121
Source/NSLock.m
121
Source/NSLock.m
|
@ -1,8 +1,9 @@
|
||||||
/** Mutual exclusion locking classes
|
/** Mutual exclusion locking classes
|
||||||
Copyright (C) 1996 Free Software Foundation, Inc.
|
Copyright (C) 1996,2003 Free Software Foundation, Inc.
|
||||||
|
|
||||||
Author: Scott Christley <scottc@net-community.com>
|
Author: Scott Christley <scottc@net-community.com>
|
||||||
Created: 1996
|
Created: 1996
|
||||||
|
Author: Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
This file is part of the GNUstep Objective-C Library.
|
This file is part of the GNUstep Objective-C Library.
|
||||||
|
|
||||||
|
@ -33,6 +34,63 @@
|
||||||
#include "Foundation/NSLock.h"
|
#include "Foundation/NSLock.h"
|
||||||
#include "Foundation/NSException.h"
|
#include "Foundation/NSException.h"
|
||||||
#include "Foundation/NSDebug.h"
|
#include "Foundation/NSDebug.h"
|
||||||
|
#include "Foundation/NSThread.h"
|
||||||
|
|
||||||
|
extern void GSSleepUntilIntervalSinceReferenceDate(NSTimeInterval);
|
||||||
|
extern NSTimeInterval GSTimeNow();
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
NSTimeInterval end;
|
||||||
|
NSTimeInterval i0;
|
||||||
|
NSTimeInterval i1;
|
||||||
|
NSTimeInterval max;
|
||||||
|
} GSSleepInfo;
|
||||||
|
|
||||||
|
static void GSSleepInit(NSDate *limit, GSSleepInfo *context)
|
||||||
|
{
|
||||||
|
context->end = [limit timeIntervalSinceReferenceDate];
|
||||||
|
context->i0 = 0.0;
|
||||||
|
context->i1 = 0.0001; // Initial pause interval.
|
||||||
|
context->max = 0.25; // Maximum pause interval.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Using a pointer to a context structure initialised using GSSleepInit()
|
||||||
|
* we either pause for a while and return YES or, if the limit date
|
||||||
|
* has passed, return NO.
|
||||||
|
* </p>
|
||||||
|
* <p>The pause intervals start off very small, but rapidly increase
|
||||||
|
* (following a fibonacci sequence) up to a maximum value.
|
||||||
|
* </p>
|
||||||
|
* <p>We use the GSSleepUntilIntervalSinceReferenceDate() function to
|
||||||
|
* avoid objc runtime messaging overheads and overheads of creating and
|
||||||
|
* destroying temporary date objects.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
static BOOL GSSleepOrFail(GSSleepInfo *context)
|
||||||
|
{
|
||||||
|
NSTimeInterval when = GSTimeNow();
|
||||||
|
NSTimeInterval tmp;
|
||||||
|
|
||||||
|
if (when >= context->end)
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
tmp = context->i0 + context->i1;
|
||||||
|
context->i0 = context->i1;
|
||||||
|
context->i1 = tmp;
|
||||||
|
if (tmp > context->max)
|
||||||
|
{
|
||||||
|
tmp = context->max;
|
||||||
|
}
|
||||||
|
when += tmp;
|
||||||
|
if (when > context->end)
|
||||||
|
{
|
||||||
|
when = context->end;
|
||||||
|
}
|
||||||
|
GSSleepUntilIntervalSinceReferenceDate(when);
|
||||||
|
return YES; // Paused.
|
||||||
|
}
|
||||||
|
|
||||||
// Exceptions
|
// Exceptions
|
||||||
|
|
||||||
|
@ -145,32 +203,22 @@ NSString *NSRecursiveLockException = @"NSRecursiveLockException";
|
||||||
* has the lock (but it waits until the time limit is up before returning
|
* has the lock (but it waits until the time limit is up before returning
|
||||||
* NO).
|
* NO).
|
||||||
*/
|
*/
|
||||||
- (BOOL) lockBeforeDate: (NSDate *)limit
|
- (BOOL) lockBeforeDate: (NSDate*)limit
|
||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
|
GSSleepInfo ctxt;
|
||||||
|
|
||||||
|
GSSleepInit(limit, &ctxt);
|
||||||
|
|
||||||
/* This is really the behavior of OpenStep, if the current thread has
|
/* This is really the behavior of OpenStep, if the current thread has
|
||||||
the lock, we just block until the time limit is up. Very odd */
|
the lock, we just block until the time limit is up. Very odd */
|
||||||
while (_mutex->owner == objc_thread_id()
|
while (_mutex->owner == objc_thread_id()
|
||||||
|| (x = objc_mutex_trylock(_mutex)) == -1)
|
|| (x = objc_mutex_trylock(_mutex)) == -1)
|
||||||
{
|
{
|
||||||
NSDate *current = [NSDate date];
|
if (GSSleepOrFail(&ctxt) == NO)
|
||||||
NSComparisonResult compare;
|
|
||||||
|
|
||||||
compare = [current compare: limit];
|
|
||||||
if (compare == NSOrderedSame || compare == NSOrderedDescending)
|
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
#if defined(__MINGW__)
|
|
||||||
Sleep(250); // 0.25 second
|
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* This should probably be more accurate like usleep(250)
|
|
||||||
* but usleep is known to NOT be thread safe under all architectures.
|
|
||||||
*/
|
|
||||||
sleep(1);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
@ -378,27 +426,18 @@ NSString *NSRecursiveLockException = @"NSRecursiveLockException";
|
||||||
// Acquiring the lock with a date condition
|
// Acquiring the lock with a date condition
|
||||||
- (BOOL) lockBeforeDate: (NSDate*)limit
|
- (BOOL) lockBeforeDate: (NSDate*)limit
|
||||||
{
|
{
|
||||||
|
GSSleepInfo ctxt;
|
||||||
|
|
||||||
CHECK_RECURSIVE_CONDITION_LOCK(_mutex);
|
CHECK_RECURSIVE_CONDITION_LOCK(_mutex);
|
||||||
|
|
||||||
|
GSSleepInit(limit, &ctxt);
|
||||||
|
|
||||||
while (objc_mutex_trylock(_mutex) == -1)
|
while (objc_mutex_trylock(_mutex) == -1)
|
||||||
{
|
{
|
||||||
NSDate *current = [NSDate date];
|
if (GSSleepOrFail(&ctxt) == NO)
|
||||||
NSComparisonResult compare;
|
|
||||||
|
|
||||||
compare = [current compare: limit];
|
|
||||||
if (compare == NSOrderedSame || compare == NSOrderedDescending)
|
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
#if defined(__MINGW__)
|
|
||||||
Sleep(250); // 0.25 second
|
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* This should probably be more accurate like usleep(250)
|
|
||||||
* but usleep is known to NOT be thread safe under all architectures.
|
|
||||||
*/
|
|
||||||
sleep(1);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
@ -556,27 +595,17 @@ NSString *NSRecursiveLockException = @"NSRecursiveLockException";
|
||||||
* YES if it can. It returns NO if it cannot
|
* YES if it can. It returns NO if it cannot
|
||||||
* (but it waits until the time limit is up before returning NO).
|
* (but it waits until the time limit is up before returning NO).
|
||||||
*/
|
*/
|
||||||
- (BOOL) lockBeforeDate: (NSDate *)limit
|
- (BOOL) lockBeforeDate: (NSDate*)limit
|
||||||
{
|
{
|
||||||
|
GSSleepInfo ctxt;
|
||||||
|
|
||||||
|
GSSleepInit(limit, &ctxt);
|
||||||
while (objc_mutex_trylock(_mutex) == -1)
|
while (objc_mutex_trylock(_mutex) == -1)
|
||||||
{
|
{
|
||||||
NSDate *current = [NSDate date];
|
if (GSSleepOrFail(&ctxt) == NO)
|
||||||
NSComparisonResult compare;
|
|
||||||
|
|
||||||
compare = [current compare: limit];
|
|
||||||
if (compare == NSOrderedSame || compare == NSOrderedDescending)
|
|
||||||
{
|
{
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
#if defined(__MINGW__)
|
|
||||||
Sleep(250); // 0.25 second
|
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* This should probably be more accurate like usleep(250)
|
|
||||||
* but usleep is known to NOT be thread safe under all architectures.
|
|
||||||
*/
|
|
||||||
sleep(1);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,94 @@
|
||||||
static Class threadClass = Nil;
|
static Class threadClass = Nil;
|
||||||
static NSNotificationCenter *nc = nil;
|
static NSNotificationCenter *nc = nil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sleep until the current date/time is the specified time interval
|
||||||
|
* past the reference date/time.<br />
|
||||||
|
* Implemented as a function taking an NSTimeInterval argument in order
|
||||||
|
* to avoid objc messaging and object allocation/deallocation (NSDate)
|
||||||
|
* overheads.<br />
|
||||||
|
* Used to implement [NSThread+sleepUntilDate:]
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
GSSleepUntilIntervalSinceReferenceDate(NSTimeInterval when)
|
||||||
|
{
|
||||||
|
extern NSTimeInterval GSTimeNow();
|
||||||
|
NSTimeInterval delay;
|
||||||
|
|
||||||
|
// delay is always the number of seconds we still need to wait
|
||||||
|
delay = when - GSTimeNow();
|
||||||
|
|
||||||
|
#ifdef HAVE_NANOSLEEP
|
||||||
|
// Avoid any possibility of overflow by sleeping in chunks.
|
||||||
|
while (delay > 32768)
|
||||||
|
{
|
||||||
|
struct timespec request;
|
||||||
|
|
||||||
|
request.tv_sec = (time_t)32768;
|
||||||
|
request.tv_nsec = (long)0;
|
||||||
|
nanosleep(&request, 0);
|
||||||
|
delay = when - GSTimeNow();
|
||||||
|
}
|
||||||
|
if (delay > 0)
|
||||||
|
{
|
||||||
|
struct timespec request;
|
||||||
|
struct timespec remainder;
|
||||||
|
|
||||||
|
request.tv_sec = (time_t)delay;
|
||||||
|
request.tv_nsec = (long)((delay - request.tv_sec) * 1000000000);
|
||||||
|
remainder.tv_sec = 0;
|
||||||
|
remainder.tv_nsec = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* With nanosleep, we can restart the sleep after a signal by using
|
||||||
|
* the remainder information ... so we can be sure to sleep to the
|
||||||
|
* desired limit without having to re-generate the delay needed.
|
||||||
|
*/
|
||||||
|
while (nanosleep(&request, &remainder) < 0
|
||||||
|
&& (remainder.tv_sec > 0 || remainder.tv_nsec > 0))
|
||||||
|
{
|
||||||
|
request.tv_sec = remainder.tv_sec;
|
||||||
|
request.tv_nsec = remainder.tv_nsec;
|
||||||
|
remainder.tv_sec = 0;
|
||||||
|
remainder.tv_nsec = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Avoid integer overflow by breaking up long sleeps.
|
||||||
|
*/
|
||||||
|
while (delay > 30.0*60.0)
|
||||||
|
{
|
||||||
|
// sleep 30 minutes
|
||||||
|
#if defined(__MINGW__)
|
||||||
|
Sleep (30*60*1000);
|
||||||
|
#else
|
||||||
|
sleep (30*60);
|
||||||
|
#endif
|
||||||
|
delay = when - GSTimeNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sleeping may return early because of signals, so we need to re-calculate
|
||||||
|
* the required delay and check to see if we need to sleep again.
|
||||||
|
*/
|
||||||
|
while (delay > 0)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_USLEEP
|
||||||
|
usleep ((int)(delay*1000000));
|
||||||
|
#else
|
||||||
|
#if defined(__MINGW__)
|
||||||
|
Sleep (delay*1000);
|
||||||
|
#else
|
||||||
|
sleep ((int)delay);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
delay = when - GSTimeNow();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static NSArray *
|
static NSArray *
|
||||||
commonModes()
|
commonModes()
|
||||||
{
|
{
|
||||||
|
@ -488,48 +576,10 @@ gnustep_base_thread_callback()
|
||||||
*/
|
*/
|
||||||
+ (void) sleepUntilDate: (NSDate*)date
|
+ (void) sleepUntilDate: (NSDate*)date
|
||||||
{
|
{
|
||||||
NSTimeInterval delay;
|
GSSleepUntilIntervalSinceReferenceDate([date timeIntervalSinceReferenceDate]);
|
||||||
|
|
||||||
// delay is always the number of seconds we still need to wait
|
|
||||||
delay = [date timeIntervalSinceNow];
|
|
||||||
|
|
||||||
// Avoid integer overflow by breaking up long sleeps
|
|
||||||
// We assume usleep can accept a value at least 31 bits in length
|
|
||||||
while (delay > 30.0*60.0)
|
|
||||||
{
|
|
||||||
// sleep 30 minutes
|
|
||||||
#if defined(__MINGW__)
|
|
||||||
Sleep (30*60*1000);
|
|
||||||
#else
|
|
||||||
sleep (30*60);
|
|
||||||
#endif
|
|
||||||
delay = [date timeIntervalSinceNow];
|
|
||||||
}
|
|
||||||
|
|
||||||
// sleeping may return early because of signals
|
|
||||||
while (delay > 0)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_NANOSLEEP
|
|
||||||
struct timespec req;
|
|
||||||
|
|
||||||
req.tv_sec = (time_t)delay;
|
|
||||||
req.tv_nsec = (long)((delay - req.tv_sec) * 1000000000);
|
|
||||||
nanosleep(&req, 0);
|
|
||||||
#else
|
|
||||||
#ifdef HAVE_USLEEP
|
|
||||||
usleep ((int)(delay*1000000));
|
|
||||||
#else
|
|
||||||
#if defined(__MINGW__)
|
|
||||||
Sleep (delay*1000);
|
|
||||||
#else
|
|
||||||
sleep ((int)delay);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
delay = [date timeIntervalSinceNow];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the priority of the current thread.
|
* Return the priority of the current thread.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include <Foundation/Foundation.h>
|
#include <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NSLock *lock = nil;
|
||||||
|
|
||||||
@interface XX : NSObject
|
@interface XX : NSObject
|
||||||
- (void) fire;
|
- (void) fire;
|
||||||
- (void) setup;
|
- (void) setup;
|
||||||
|
@ -14,19 +16,27 @@
|
||||||
{
|
{
|
||||||
CREATE_AUTORELEASE_POOL(arp);
|
CREATE_AUTORELEASE_POOL(arp);
|
||||||
|
|
||||||
NSLog(@"Setup1");
|
NSLog(@"Attempting to obtain lock to proceed");
|
||||||
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
|
if ([lock lockBeforeDate: [NSDate dateWithTimeIntervalSinceNow: 5.0]] == YES)
|
||||||
NSLog(@"Setup2");
|
{
|
||||||
[self performSelectorOnMainThread: @selector(fire)
|
NSLog(@"Setup1");
|
||||||
withObject: nil
|
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
|
||||||
waitUntilDone: NO];
|
NSLog(@"Setup2");
|
||||||
NSLog(@"Done perform no wait.");
|
[self performSelectorOnMainThread: @selector(fire)
|
||||||
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
|
withObject: nil
|
||||||
NSLog(@"Setup3");
|
waitUntilDone: NO];
|
||||||
[self performSelectorOnMainThread: @selector(fire)
|
NSLog(@"Done perform no wait.");
|
||||||
withObject: nil
|
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
|
||||||
waitUntilDone: YES];
|
NSLog(@"Setup3");
|
||||||
NSLog(@"Done perform with wait.");
|
[self performSelectorOnMainThread: @selector(fire)
|
||||||
|
withObject: nil
|
||||||
|
waitUntilDone: YES];
|
||||||
|
NSLog(@"Done perform with wait.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSLog(@"Failed to obtain lock");
|
||||||
|
}
|
||||||
RELEASE(arp);
|
RELEASE(arp);
|
||||||
[NSThread exit];
|
[NSThread exit];
|
||||||
}
|
}
|
||||||
|
@ -34,17 +44,26 @@
|
||||||
|
|
||||||
int main(int argc, char **argv, char **env)
|
int main(int argc, char **argv, char **env)
|
||||||
{
|
{
|
||||||
id arp = [NSAutoreleasePool new];
|
CREATE_AUTORELEASE_POOL(arp);
|
||||||
|
|
||||||
NSLog(@"Start in main");
|
NSLog(@"Start in main");
|
||||||
|
lock = [NSLock new];
|
||||||
|
[lock lock];
|
||||||
|
|
||||||
[NSThread detachNewThreadSelector: @selector(setup)
|
[NSThread detachNewThreadSelector: @selector(setup)
|
||||||
toTarget: [XX new]
|
toTarget: [XX new]
|
||||||
withObject: nil];
|
withObject: nil];
|
||||||
|
NSLog(@"Waiting to give thread time to start");
|
||||||
|
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
|
||||||
|
NSLog(@"Releasing lock so thread may proceed");
|
||||||
|
[lock unlock]; // Allow other thread to proceed.
|
||||||
|
|
||||||
[[NSRunLoop currentRunLoop] runUntilDate:
|
[[NSRunLoop currentRunLoop] runUntilDate:
|
||||||
[NSDate dateWithTimeIntervalSinceNow: 10.0]];
|
[NSDate dateWithTimeIntervalSinceNow: 10.0]];
|
||||||
|
|
||||||
NSLog(@"Done main thread");
|
NSLog(@"Done main thread");
|
||||||
|
|
||||||
|
DESTROY(arp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue