Index: src/OFString+PathAdditions.m ================================================================== --- src/OFString+PathAdditions.m +++ src/OFString+PathAdditions.m @@ -15,16 +15,16 @@ * file. */ #include "config.h" -#import "platform.h" +#include "platform.h" #if defined(OF_WINDOWS) || defined(OF_MSDOS) -# import "OFString+PathAdditions_DOS.m" +# import "platform/windows/OFString+PathAdditions.m" #elif defined(OF_AMIGAOS) -# import "OFString+PathAdditions_AmigaOS.m" +# import "platform/amiga/OFString+PathAdditions.m" #elif defined(OF_NINTENDO_3DS) || defined(OF_WII) -# import "OFString+PathAdditions_libfat.m" +# import "platform/libfat/OFString+PathAdditions.m" #else -# import "OFString+PathAdditions_UNIX.m" +# import "platform/posix/OFString+PathAdditions.m" #endif DELETED src/OFString+PathAdditions_AmigaOS.m Index: src/OFString+PathAdditions_AmigaOS.m ================================================================== --- src/OFString+PathAdditions_AmigaOS.m +++ src/OFString+PathAdditions_AmigaOS.m @@ -1,273 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#import "OFString+PathAdditions.h" -#import "OFArray.h" - -#import "OFOutOfRangeException.h" - -int _OFString_PathAdditions_reference; - -@implementation OFString (PathAdditions) -+ (OFString *)pathWithComponents: (OFArray *)components -{ - OFMutableString *ret = [OFMutableString string]; - void *pool = objc_autoreleasePoolPush(); - bool firstAfterDevice = true; - - for (OFString *component in components) { - if (component.length == 0) - continue; - - if (!firstAfterDevice) - [ret appendString: @"/"]; - - [ret appendString: component]; - - if (![component hasSuffix: @":"]) - firstAfterDevice = false; - } - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (bool)isAbsolutePath -{ - return [self containsString: @":"]; -} - -- (OFArray *)pathComponents -{ - OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; - void *pool = objc_autoreleasePoolPush(); - const char *cString = self.UTF8String; - size_t i, last = 0, cStringLength = self.UTF8StringLength; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return ret; - } - - for (i = 0; i < cStringLength; i++) { - if (cString[i] == '/') { - if (i - last != 0) - [ret addObject: [OFString - stringWithUTF8String: cString + last - length: i - last]]; - else - [ret addObject: @"/"]; - - last = i + 1; - } else if (cString[i] == ':') { - [ret addObject: [OFString - stringWithUTF8String: cString + last - length: i - last + 1]]; - - last = i + 1; - } - } - if (i - last != 0) - [ret addObject: [OFString stringWithUTF8String: cString + last - length: i - last]]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (OFString *)lastPathComponent -{ - /* - * AmigaOS needs the full parsing to determine the last path component. - * This could be optimized by not creating the temporary objects, - * though. - */ - void *pool = objc_autoreleasePoolPush(); - OFString *ret = self.pathComponents.lastObject; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)pathExtension -{ - void *pool = objc_autoreleasePoolPush(); - OFString *ret, *fileName; - size_t pos; - - fileName = self.lastPathComponent; - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - ret = [fileName substringWithRange: - of_range(pos + 1, fileName.length - pos - 1)]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByDeletingLastPathComponent -{ - /* - * AmigaOS needs the full parsing to delete the last path component. - * This could be optimized, though. - */ - void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFString *) *components = self.pathComponents; - size_t count = components.count; - OFString *ret; - - if (count < 2) { - if ([components.firstObject hasSuffix: @":"]) { - ret = [components.firstObject retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; - } - - objc_autoreleasePoolPop(pool); - return @""; - } - - components = [components objectsInRange: - of_range(0, components.count - 1)]; - ret = [OFString pathWithComponents: components]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByDeletingPathExtension -{ - void *pool; - OFMutableArray OF_GENERIC(OFString *) *components; - OFString *ret, *fileName; - size_t pos; - - if (self.length == 0) - return [[self copy] autorelease]; - - pool = objc_autoreleasePoolPush(); - components = [[self.pathComponents mutableCopy] autorelease]; - fileName = components.lastObject; - - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - fileName = [fileName substringWithRange: of_range(0, pos)]; - [components replaceObjectAtIndex: components.count - 1 - withObject: fileName]; - - ret = [OFString pathWithComponents: components]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByStandardizingPath -{ - void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFString *) *components; - OFMutableArray OF_GENERIC(OFString *) *array; - OFString *ret; - bool done = false; - - if (self.length == 0) - return @""; - - components = self.pathComponents; - - if (components.count == 1) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - array = [[components mutableCopy] autorelease]; - - while (!done) { - size_t length = array.count; - - done = true; - - for (size_t i = 0; i < length; i++) { - OFString *component = [array objectAtIndex: i]; - OFString *parent = - (i > 0 ? [array objectAtIndex: i - 1] : 0); - - if (component.length == 0) { - [array removeObjectAtIndex: i]; - - done = false; - break; - } - - if ([component isEqual: @"/"] && - parent != nil && ![parent isEqual: @"/"]) { - [array removeObjectsInRange: - of_range(i - 1, 2)]; - - done = false; - break; - } - } - } - - ret = [OFString pathWithComponents: array]; - - if ([self hasSuffix: @"/"]) - ret = [ret stringByAppendingString: @"/"]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByAppendingPathComponent: (OFString *)component -{ - if ([self hasSuffix: @"/"] || [self hasSuffix: @":"]) - return [self stringByAppendingString: component]; - else { - OFMutableString *ret = [[self mutableCopy] autorelease]; - - [ret appendString: @"/"]; - [ret appendString: component]; - - [ret makeImmutable]; - - return ret; - } -} -@end DELETED src/OFString+PathAdditions_DOS.m Index: src/OFString+PathAdditions_DOS.m ================================================================== --- src/OFString+PathAdditions_DOS.m +++ src/OFString+PathAdditions_DOS.m @@ -1,314 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#import "OFString+PathAdditions.h" -#import "OFArray.h" - -#import "OFOutOfRangeException.h" - -int _OFString_PathAdditions_reference; - -@implementation OFString (PathAdditions) -+ (OFString *)pathWithComponents: (OFArray *)components -{ - OFMutableString *ret = [OFMutableString string]; - void *pool = objc_autoreleasePoolPush(); - bool first = true; - - for (OFString *component in components) { - if (component.length == 0) - continue; - - if (!first && ![ret hasSuffix: @":"] && - ([component isEqual: @"\\"] || [component isEqual: @"/"])) - continue; - - if (!first && ![ret hasSuffix: @"\\"] && - ![ret hasSuffix: @"/"] && ![ret hasSuffix: @":"]) - [ret appendString: @"\\"]; - - [ret appendString: component]; - - first = false; - } - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (bool)isAbsolutePath -{ -#ifdef OF_WINDOWS - if ([self hasPrefix: @"\\\\"]) - return true; -#endif - - return ([self containsString: @":\\"] || [self containsString: @":/"]); -} - -- (OFArray *)pathComponents -{ - OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; - void *pool = objc_autoreleasePoolPush(); - const char *cString = self.UTF8String; - size_t i, last = 0, cStringLength = self.UTF8StringLength; - bool isUNC = false; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return ret; - } - - if ([self hasPrefix: @"\\\\"]) { - isUNC = true; - [ret addObject: @"\\\\"]; - - cString += 2; - cStringLength -= 2; - } - - for (i = 0; i < cStringLength; i++) { - if (cString[i] == '\\' || cString[i] == '/') { - if (i == 0) - [ret addObject: [OFString - stringWithUTF8String: cString - length: 1]]; - else if (i - last != 0) - [ret addObject: [OFString - stringWithUTF8String: cString + last - length: i - last]]; - - last = i + 1; - } else if (!isUNC && cString[i] == ':') { - if (i + 1 < cStringLength && - (cString[i + 1] == '\\' || cString[i + 1] == '/')) - i++; - - [ret addObject: [OFString - stringWithUTF8String: cString + last - length: i - last + 1]]; - - last = i + 1; - } - } - if (i - last != 0) - [ret addObject: [OFString stringWithUTF8String: cString + last - length: i - last]]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (OFString *)lastPathComponent -{ - /* - * Windows/DOS need the full parsing to determine the last path - * component. This could be optimized by not creating the temporary - * objects, though. - */ - void *pool = objc_autoreleasePoolPush(); - OFString *ret = self.pathComponents.lastObject; - - if (ret == nil) { - objc_autoreleasePoolPop(pool); - return @""; - } - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)pathExtension -{ - void *pool = objc_autoreleasePoolPush(); - OFString *ret, *fileName; - size_t pos; - - fileName = self.lastPathComponent; - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - ret = [fileName substringWithRange: - of_range(pos + 1, fileName.length - pos - 1)]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByDeletingLastPathComponent -{ - /* - * Windows/DOS need the full parsing to delete the last path component. - * This could be optimized, though. - */ - void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFString *) *components = self.pathComponents; - size_t count = components.count; - OFString *ret; - - if (count == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - if (count == 1) { - OFString *firstComponent = components.firstObject; - - if ([firstComponent hasSuffix: @":"] || - [firstComponent hasSuffix: @":\\"] || - [firstComponent hasSuffix: @":/"] || - [firstComponent hasPrefix: @"\\"]) { - ret = [firstComponent retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; - } - - objc_autoreleasePoolPop(pool); - return @"."; - } - - components = [components objectsInRange: - of_range(0, components.count - 1)]; - ret = [OFString pathWithComponents: components]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByDeletingPathExtension -{ - void *pool; - OFMutableArray OF_GENERIC(OFString *) *components; - OFString *ret, *fileName; - size_t pos; - - if (self.length == 0) - return [[self copy] autorelease]; - - pool = objc_autoreleasePoolPush(); - components = [[self.pathComponents mutableCopy] autorelease]; - fileName = components.lastObject; - - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - fileName = [fileName substringWithRange: of_range(0, pos)]; - [components replaceObjectAtIndex: components.count - 1 - withObject: fileName]; - - ret = [OFString pathWithComponents: components]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByStandardizingPath -{ - void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFString *) *components; - OFMutableArray OF_GENERIC(OFString *) *array; - OFString *ret; - bool done = false; - - if (self.length == 0) - return @""; - - components = self.pathComponents; - - if (components.count == 1) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - array = [[components mutableCopy] autorelease]; - - while (!done) { - size_t length = array.count; - - done = true; - - for (size_t i = 0; i < length; i++) { - OFString *component = [array objectAtIndex: i]; - OFString *parent = - (i > 0 ? [array objectAtIndex: i - 1] : 0); - - if ([component isEqual: @"."] || - component.length == 0) { - [array removeObjectAtIndex: i]; - - done = false; - break; - } - - if ([component isEqual: @".."] && parent != nil && - ![parent isEqual: @".."] && - ![parent hasSuffix: @":"] && - ![parent hasSuffix: @":\\"] && - ![parent hasSuffix: @"://"] && - (![parent hasPrefix: @"\\"] || i != 1)) { - [array removeObjectsInRange: - of_range(i - 1, 2)]; - - done = false; - break; - } - } - } - - ret = [[OFString pathWithComponents: array] retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)stringByAppendingPathComponent: (OFString *)component -{ - if ([self hasSuffix: @"\\"] || [self hasSuffix: @"/"]) - return [self stringByAppendingString: component]; - else { - OFMutableString *ret = [[self mutableCopy] autorelease]; - - [ret appendString: @"\\"]; - [ret appendString: component]; - - [ret makeImmutable]; - - return ret; - } -} -@end DELETED src/OFString+PathAdditions_UNIX.m Index: src/OFString+PathAdditions_UNIX.m ================================================================== --- src/OFString+PathAdditions_UNIX.m +++ src/OFString+PathAdditions_UNIX.m @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#import "OFString+PathAdditions.h" -#import "OFArray.h" - -#import "OFOutOfRangeException.h" - -int _OFString_PathAdditions_reference; - -@implementation OFString (PathAdditions) -+ (OFString *)pathWithComponents: (OFArray *)components -{ - OFMutableString *ret = [OFMutableString string]; - void *pool = objc_autoreleasePoolPush(); - bool first = true; - - for (OFString *component in components) { - if (component.length == 0) - continue; - - if (!first && [component isEqual: @"/"]) - continue; - - if (!first && ![ret hasSuffix: @"/"]) - [ret appendString: @"/"]; - - [ret appendString: component]; - - first = false; - } - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (bool)isAbsolutePath -{ - return [self hasPrefix: @"/"]; -} - -- (OFArray *)pathComponents -{ - OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; - void *pool = objc_autoreleasePoolPush(); - const char *cString = self.UTF8String; - size_t i, last = 0, cStringLength = self.UTF8StringLength; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return ret; - } - - for (i = 0; i < cStringLength; i++) { - if (cString[i] == '/') { - if (i == 0) - [ret addObject: @"/"]; - else if (i - last != 0) - [ret addObject: [OFString - stringWithUTF8String: cString + last - length: i - last]]; - - last = i + 1; - } - } - if (i - last != 0) - [ret addObject: [OFString stringWithUTF8String: cString + last - length: i - last]]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (OFString *)lastPathComponent -{ - void *pool = objc_autoreleasePoolPush(); - const char *cString = self.UTF8String; - size_t cStringLength = self.UTF8StringLength; - ssize_t i; - OFString *ret; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - if (cString[cStringLength - 1] == '/') - cStringLength--; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @"/"; - } - - if (cStringLength - 1 > SSIZE_MAX) - @throw [OFOutOfRangeException exception]; - - for (i = cStringLength - 1; i >= 0; i--) { - if (cString[i] == '/') { - i++; - break; - } - } - - /* - * Only one component, but the trailing delimiter might have been - * removed, so return a new string anyway. - */ - if (i < 0) - i = 0; - - ret = [[OFString alloc] initWithUTF8String: cString + i - length: cStringLength - i]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)pathExtension -{ - void *pool = objc_autoreleasePoolPush(); - OFString *ret, *fileName; - size_t pos; - - fileName = self.lastPathComponent; - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - ret = [fileName substringWithRange: - of_range(pos + 1, fileName.length - pos - 1)]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByDeletingLastPathComponent -{ - void *pool = objc_autoreleasePoolPush(); - const char *cString = self.UTF8String; - size_t cStringLength = self.UTF8StringLength; - OFString *ret; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - if (cString[cStringLength - 1] == '/') - cStringLength--; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @"/"; - } - - for (size_t i = cStringLength; i >= 1; i--) { - if (cString[i - 1] == '/') { - if (i == 1) { - objc_autoreleasePoolPop(pool); - return @"/"; - } - - ret = [[OFString alloc] initWithUTF8String: cString - length: i - 1]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; - } - } - - objc_autoreleasePoolPop(pool); - - return @"."; -} - -- (OFString *)stringByDeletingPathExtension -{ - void *pool; - OFMutableArray OF_GENERIC(OFString *) *components; - OFString *ret, *fileName; - size_t pos; - - if (self.length == 0) - return [[self copy] autorelease]; - - pool = objc_autoreleasePoolPush(); - components = [[self.pathComponents mutableCopy] autorelease]; - fileName = components.lastObject; - - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - fileName = [fileName substringWithRange: of_range(0, pos)]; - [components replaceObjectAtIndex: [components count] - 1 - withObject: fileName]; - - ret = [OFString pathWithComponents: components]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByStandardizingPath -{ - void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFString *) *components; - OFMutableArray OF_GENERIC(OFString *) *array; - OFString *ret; - bool done = false, startsWithSlash; - - if (self.length == 0) - return @""; - - components = self.pathComponents; - - if (components.count == 1) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - array = [[components mutableCopy] autorelease]; - startsWithSlash = [self hasPrefix: @"/"]; - - if (startsWithSlash) - [array removeObjectAtIndex: 0]; - - while (!done) { - size_t length = array.count; - - done = true; - - for (size_t i = 0; i < length; i++) { - OFString *component = [array objectAtIndex: i]; - OFString *parent = - (i > 0 ? [array objectAtIndex: i - 1] : 0); - - if ([component isEqual: @"."] || - component.length == 0) { - [array removeObjectAtIndex: i]; - - done = false; - break; - } - - if ([component isEqual: @".."] && - parent != nil && ![parent isEqual: @".."]) { - [array removeObjectsInRange: - of_range(i - 1, 2)]; - - done = false; - break; - } - } - } - - if (startsWithSlash) - [array insertObject: @"" - atIndex: 0]; - - if ([self hasSuffix: @"/"]) - [array addObject: @""]; - - ret = [[array componentsJoinedByString: @"/"] retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)stringByAppendingPathComponent: (OFString *)component -{ - if ([self hasSuffix: @"/"]) - return [self stringByAppendingString: component]; - else { - OFMutableString *ret = [[self mutableCopy] autorelease]; - - [ret appendString: @"/"]; - [ret appendString: component]; - - [ret makeImmutable]; - - return ret; - } -} -@end DELETED src/OFString+PathAdditions_libfat.m Index: src/OFString+PathAdditions_libfat.m ================================================================== --- src/OFString+PathAdditions_libfat.m +++ src/OFString+PathAdditions_libfat.m @@ -1,319 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#import "OFString+PathAdditions.h" -#import "OFArray.h" - -#import "OFOutOfRangeException.h" - -int _OFString_PathAdditions_reference; - -@implementation OFString (PathAdditions) -+ (OFString *)pathWithComponents: (OFArray *)components -{ - OFMutableString *ret = [OFMutableString string]; - void *pool = objc_autoreleasePoolPush(); - bool first = true; - - for (OFString *component in components) { - if (component.length == 0) - continue; - - if ([component isEqual: @"/"]) - continue; - - if (!first && ![ret hasSuffix: @"/"]) - [ret appendString: @"/"]; - - [ret appendString: component]; - - first = false; - } - - if ([ret hasSuffix: @":"]) - [ret appendString: @"/"]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (bool)isAbsolutePath -{ - return [self containsString: @":/"]; -} - -- (OFArray *)pathComponents -{ - OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; - void *pool = objc_autoreleasePoolPush(); - const char *cString = self.UTF8String; - size_t i, last = 0, cStringLength = self.UTF8StringLength; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return ret; - } - - for (i = 0; i < cStringLength; i++) { - if (cString[i] == '/') { - if (i - last != 0) - [ret addObject: [OFString - stringWithUTF8String: cString + last - length: i - last]]; - - last = i + 1; - } - } - if (i - last != 0) - [ret addObject: [OFString stringWithUTF8String: cString + last - length: i - last]]; - - [ret makeImmutable]; - - objc_autoreleasePoolPop(pool); - - return ret; -} - -- (OFString *)lastPathComponent -{ - void *pool = objc_autoreleasePoolPush(); - const char *cString; - size_t cStringLength; - ssize_t i; - OFString *ret; - - if ([self hasSuffix: @":/"]) - return self; - - cString = self.UTF8String; - cStringLength = self.UTF8StringLength; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - if (cString[cStringLength - 1] == '/') - cStringLength--; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - if (cStringLength - 1 > SSIZE_MAX) - @throw [OFOutOfRangeException exception]; - - for (i = cStringLength - 1; i >= 0; i--) { - if (cString[i] == '/') { - i++; - break; - } - } - - /* - * Only one component, but the trailing delimiter might have been - * removed, so return a new string anyway. - */ - if (i < 0) - i = 0; - - ret = [[OFString alloc] initWithUTF8String: cString + i - length: cStringLength - i]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)pathExtension -{ - void *pool = objc_autoreleasePoolPush(); - OFString *ret, *fileName; - size_t pos; - - fileName = self.lastPathComponent; - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - ret = [fileName substringWithRange: - of_range(pos + 1, fileName.length - pos - 1)]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByDeletingLastPathComponent -{ - void *pool = objc_autoreleasePoolPush(); - const char *cString; - size_t cStringLength; - OFString *ret; - - if ([self hasSuffix: @":/"]) - return self; - - cString = self.UTF8String; - cStringLength = self.UTF8StringLength; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - if (cString[cStringLength - 1] == '/') - cStringLength--; - - if (cStringLength == 0) { - objc_autoreleasePoolPop(pool); - return @""; - } - - for (size_t i = cStringLength; i >= 1; i--) { - if (cString[i - 1] == '/') { - ret = [[OFString alloc] initWithUTF8String: cString - length: i - 1]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; - } - } - - objc_autoreleasePoolPop(pool); - - return @"."; -} - -- (OFString *)stringByDeletingPathExtension -{ - void *pool; - OFMutableArray OF_GENERIC(OFString *) *components; - OFString *ret, *fileName; - size_t pos; - - if (self.length == 0) - return [[self copy] autorelease]; - - pool = objc_autoreleasePoolPush(); - components = [[self.pathComponents mutableCopy] autorelease]; - fileName = components.lastObject; - - pos = [fileName rangeOfString: @"." - options: OF_STRING_SEARCH_BACKWARDS].location; - if (pos == OF_NOT_FOUND || pos == 0) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - fileName = [fileName substringWithRange: of_range(0, pos)]; - [components replaceObjectAtIndex: components.count - 1 - withObject: fileName]; - - ret = [OFString pathWithComponents: components]; - - [ret retain]; - objc_autoreleasePoolPop(pool); - return [ret autorelease]; -} - -- (OFString *)stringByStandardizingPath -{ - void *pool = objc_autoreleasePoolPush(); - OFArray OF_GENERIC(OFString *) *components; - OFMutableArray OF_GENERIC(OFString *) *array; - OFString *ret; - bool done = false; - - if (self.length == 0) - return @""; - - components = self.pathComponents; - - if (components.count == 1) { - objc_autoreleasePoolPop(pool); - return [[self copy] autorelease]; - } - - array = [[components mutableCopy] autorelease]; - - while (!done) { - size_t length = array.count; - - done = true; - - for (size_t i = 0; i < length; i++) { - OFString *component = [array objectAtIndex: i]; - OFString *parent = - (i > 0 ? [array objectAtIndex: i - 1] : 0); - - if ([component isEqual: @"."] || - component.length == 0) { - [array removeObjectAtIndex: i]; - - done = false; - break; - } - - if ([component isEqual: @".."] && - parent != nil && ![parent isEqual: @".."]) { - [array removeObjectsInRange: - of_range(i - 1, 2)]; - - done = false; - break; - } - } - } - - if ([self hasSuffix: @"/"]) - [array addObject: @""]; - - ret = [[array componentsJoinedByString: @"/"] retain]; - - objc_autoreleasePoolPop(pool); - - return [ret autorelease]; -} - -- (OFString *)stringByAppendingPathComponent: (OFString *)component -{ - if ([self hasSuffix: @"/"]) - return [self stringByAppendingString: component]; - else { - OFMutableString *ret = [[self mutableCopy] autorelease]; - - [ret appendString: @"/"]; - [ret appendString: component]; - - [ret makeImmutable]; - - return ret; - } -} -@end Index: src/condition.m ================================================================== --- src/condition.m +++ src/condition.m @@ -15,14 +15,14 @@ * file. */ #include "config.h" -#import "condition.h" +#include "platform.h" #if defined(OF_HAVE_PTHREADS) -# include "condition_pthread.m" +# include "platform/posix/condition.m" #elif defined(OF_WINDOWS) -# include "condition_winapi.m" +# include "platform/windows/condition.m" #elif defined(OF_AMIGAOS) -# include "condition_amiga.m" +# include "platform/amiga/condition.m" #endif DELETED src/condition_amiga.m Index: src/condition_amiga.m ================================================================== --- src/condition_amiga.m +++ src/condition_amiga.m @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include - -#include -#include -#ifndef OF_AMIGAOS4 -# include -#endif - -bool -of_condition_new(of_condition_t *condition) -{ - condition->waitingTasks = NULL; - - return true; -} - -bool -of_condition_signal(of_condition_t *condition) -{ - Forbid(); - @try { - if (condition->waitingTasks == NULL) - return true; - - Signal(condition->waitingTasks->task, - (1ul << condition->waitingTasks->sigBit)); - - condition->waitingTasks = condition->waitingTasks->next; - } @finally { - Permit(); - } - - return true; -} - -bool -of_condition_broadcast(of_condition_t *condition) -{ - Forbid(); - @try { - if (condition->waitingTasks == NULL) - return true; - - while (condition->waitingTasks != NULL) { - Signal(condition->waitingTasks->task, - (1ul << condition->waitingTasks->sigBit)); - - condition->waitingTasks = condition->waitingTasks->next; - } - } @finally { - Permit(); - } - - return true; -} - -bool -of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) -{ - ULONG signalMask = 0; - - return of_condition_wait_or_signal(condition, mutex, &signalMask); -} - -bool -of_condition_wait_or_signal(of_condition_t *condition, of_mutex_t *mutex, - ULONG *signalMask) -{ - struct of_condition_waiting_task waitingTask = { - .task = FindTask(NULL), - .sigBit = AllocSignal(-1) - }; - bool ret; - ULONG mask; - - if (waitingTask.sigBit == -1) { - errno = EAGAIN; - return false; - } - - Forbid(); - - if (!of_mutex_unlock(mutex)) { - FreeSignal(waitingTask.sigBit); - return false; - } - - waitingTask.next = condition->waitingTasks; - condition->waitingTasks = &waitingTask; - - mask = Wait((1ul << waitingTask.sigBit) | *signalMask); - if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask)) - ret = of_mutex_lock(mutex); - else { - /* - * This should not happen - it means something interrupted the - * Wait(), so the best we can do is return EINTR. - */ - ret = false; - errno = EINTR; - } - - FreeSignal(waitingTask.sigBit); - - Permit(); - - return ret; -} - -bool -of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, - of_time_interval_t timeout) -{ - ULONG signalMask = 0; - - return of_condition_timed_wait_or_signal(condition, mutex, timeout, - &signalMask); -} - -bool -of_condition_timed_wait_or_signal(of_condition_t *condition, of_mutex_t *mutex, - of_time_interval_t timeout, ULONG *signalMask) -{ - struct of_condition_waiting_task waitingTask = { - .task = FindTask(NULL), - .sigBit = AllocSignal(-1) - }; - struct MsgPort port = { - .mp_Node = { - .ln_Type = NT_MSGPORT - }, - .mp_Flags = PA_SIGNAL, - .mp_SigTask = waitingTask.task, - .mp_SigBit = AllocSignal(-1) - }; -#ifdef OF_AMIGAOS4 - struct TimeRequest request = { - .Request = { -#else - struct timerequest request = { - .tr_node = { -#endif - .io_Message = { - .mn_Node = { - .ln_Type = NT_MESSAGE - }, - .mn_ReplyPort = &port, - .mn_Length = sizeof(request) - }, - .io_Command = TR_ADDREQUEST - }, -#ifdef OF_AMIGAOS4 - .Time = { - .Seconds = (ULONG)timeout, - .Microseconds = - (timeout - request.Time.Seconds) * 1000000 -#else - .tr_time = { - .tv_sec = (ULONG)timeout, - .tv_micro = (timeout - request.tr_time.tv_sec) * 1000000 -#endif - } - }; - ULONG mask; - bool ret; - - NewList(&port.mp_MsgList); - - if (waitingTask.sigBit == -1 || port.mp_SigBit == -1) { - errno = EAGAIN; - goto fail; - } - - if (OpenDevice("timer.device", UNIT_MICROHZ, - (struct IORequest *)&request, 0) != 0) { - errno = EAGAIN; - goto fail; - } - - Forbid(); - - if (!of_mutex_unlock(mutex)) { - Permit(); - goto fail; - } - - waitingTask.next = condition->waitingTasks; - condition->waitingTasks = &waitingTask; - - SendIO((struct IORequest *)&request); - - mask = Wait((1ul << waitingTask.sigBit) | (1ul << port.mp_SigBit) | - *signalMask); - if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask)) - ret = of_mutex_lock(mutex); - else if (mask & (1ul << port.mp_SigBit)) { - ret = false; - errno = ETIMEDOUT; - } else { - /* - * This should not happen - it means something interrupted the - * Wait(), so the best we can do is return EINTR. - */ - ret = false; - errno = EINTR; - } - - condition->waitingTasks = waitingTask.next; - - if (!CheckIO((struct IORequest *)&request)) { - AbortIO((struct IORequest *)&request); - WaitIO((struct IORequest *)&request); - } - CloseDevice((struct IORequest *)&request); - - Permit(); - - FreeSignal(waitingTask.sigBit); - FreeSignal(port.mp_SigBit); - - return ret; - -fail: - if (waitingTask.sigBit != -1) - FreeSignal(waitingTask.sigBit); - if (port.mp_SigBit != -1) - FreeSignal(port.mp_SigBit); - - return false; -} - -bool -of_condition_free(of_condition_t *condition) -{ - Forbid(); - @try { - if (condition->waitingTasks != NULL) { - errno = EBUSY; - return false; - } - } @finally { - Permit(); - } - - return true; -} DELETED src/condition_pthread.m Index: src/condition_pthread.m ================================================================== --- src/condition_pthread.m +++ src/condition_pthread.m @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -bool -of_condition_new(of_condition_t *condition) -{ - return (pthread_cond_init(condition, NULL) == 0); -} - -bool -of_condition_signal(of_condition_t *condition) -{ - return (pthread_cond_signal(condition) == 0); -} - -bool -of_condition_broadcast(of_condition_t *condition) -{ - return (pthread_cond_broadcast(condition) == 0); -} - -bool -of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) -{ - return (pthread_cond_wait(condition, mutex) == 0); -} - -bool -of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, - of_time_interval_t timeout) -{ - struct timespec ts; - - ts.tv_sec = (time_t)timeout; - ts.tv_nsec = (timeout - ts.tv_sec) * 1000000000; - - return (pthread_cond_timedwait(condition, mutex, &ts) == 0); -} - -bool -of_condition_free(of_condition_t *condition) -{ - return (pthread_cond_destroy(condition) == 0); -} DELETED src/condition_winapi.m Index: src/condition_winapi.m ================================================================== --- src/condition_winapi.m +++ src/condition_winapi.m @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include - -bool -of_condition_new(of_condition_t *condition) -{ - condition->count = 0; - - if ((condition->event = CreateEvent(NULL, FALSE, 0, NULL)) == NULL) { - errno = EAGAIN; - return false; - } - - return true; -} - -bool -of_condition_signal(of_condition_t *condition) -{ - if (!SetEvent(condition->event)) { - switch (GetLastError()) { - case ERROR_INVALID_HANDLE: - errno = EINVAL; - return false; - default: - OF_ENSURE(0); - } - } - - return true; -} - -bool -of_condition_broadcast(of_condition_t *condition) -{ - int count = condition->count; - - for (int i = 0; i < count; i++) { - if (!SetEvent(condition->event)) { - switch (GetLastError()) { - case ERROR_INVALID_HANDLE: - errno = EINVAL; - return false; - default: - OF_ENSURE(0); - } - } - } - - return true; -} - -bool -of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) -{ - DWORD status; - - if (!of_mutex_unlock(mutex)) - return false; - - of_atomic_int_inc(&condition->count); - status = WaitForSingleObject(condition->event, INFINITE); - of_atomic_int_dec(&condition->count); - - switch (status) { - case WAIT_OBJECT_0: - return of_mutex_lock(mutex); - case WAIT_FAILED: - switch (GetLastError()) { - case ERROR_INVALID_HANDLE: - errno = EINVAL; - return false; - default: - OF_ENSURE(0); - } - default: - OF_ENSURE(0); - } -} - -bool -of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, - of_time_interval_t timeout) -{ - DWORD status; - - if (!of_mutex_unlock(mutex)) - return false; - - of_atomic_int_inc(&condition->count); - status = WaitForSingleObject(condition->event, timeout * 1000); - of_atomic_int_dec(&condition->count); - - switch (status) { - case WAIT_OBJECT_0: - return of_mutex_lock(mutex); - case WAIT_TIMEOUT: - errno = ETIMEDOUT; - return false; - case WAIT_FAILED: - switch (GetLastError()) { - case ERROR_INVALID_HANDLE: - errno = EINVAL; - return false; - default: - OF_ENSURE(0); - } - default: - OF_ENSURE(0); - } -} - -bool -of_condition_free(of_condition_t *condition) -{ - if (condition->count != 0) { - errno = EBUSY; - return false; - } - - return CloseHandle(condition->event); -} Index: src/mutex.m ================================================================== --- src/mutex.m +++ src/mutex.m @@ -15,108 +15,14 @@ * file. */ #include "config.h" -#import "mutex.h" +#include "platform.h" #if defined(OF_HAVE_PTHREADS) -# include "mutex_pthread.m" +# include "platform/posix/mutex.m" #elif defined(OF_WINDOWS) -# include "mutex_winapi.m" +# include "platform/windows/mutex.m" #elif defined(OF_AMIGAOS) -# include "mutex_amiga.m" -#endif - -#if !defined(OF_HAVE_RECURSIVE_PTHREAD_MUTEXES) && !defined(OF_WINDOWS) && \ - !defined(OF_AMIGAOS) -bool -of_rmutex_new(of_rmutex_t *rmutex) -{ - if (!of_mutex_new(&rmutex->mutex)) - return false; - - if (!of_tlskey_new(&rmutex->count)) - return false; - - return true; -} - -bool -of_rmutex_lock(of_rmutex_t *rmutex) -{ - uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); - - if (count > 0) { - if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) - return false; - - return true; - } - - if (!of_mutex_lock(&rmutex->mutex)) - return false; - - if (!of_tlskey_set(rmutex->count, (void *)1)) { - of_mutex_unlock(&rmutex->mutex); - return false; - } - - return true; -} - -bool -of_rmutex_trylock(of_rmutex_t *rmutex) -{ - uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); - - if (count > 0) { - if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) - return false; - - return true; - } - - if (!of_mutex_trylock(&rmutex->mutex)) - return false; - - if (!of_tlskey_set(rmutex->count, (void *)1)) { - of_mutex_unlock(&rmutex->mutex); - return false; - } - - return true; -} - -bool -of_rmutex_unlock(of_rmutex_t *rmutex) -{ - uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); - - if (count > 1) { - if (!of_tlskey_set(rmutex->count, (void *)(count - 1))) - return false; - - return true; - } - - if (!of_tlskey_set(rmutex->count, (void *)0)) - return false; - - if (!of_mutex_unlock(&rmutex->mutex)) - return false; - - return true; -} - -bool -of_rmutex_free(of_rmutex_t *rmutex) -{ - if (!of_mutex_free(&rmutex->mutex)) - return false; - - if (!of_tlskey_free(rmutex->count)) - return false; - - return true; -} +# include "platform/amiga/mutex.m" #endif DELETED src/mutex_amiga.m Index: src/mutex_amiga.m ================================================================== --- src/mutex_amiga.m +++ src/mutex_amiga.m @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include - -#include - -bool -of_mutex_new(of_mutex_t *mutex) -{ - InitSemaphore(mutex); - - return true; -} - -bool -of_mutex_lock(of_mutex_t *mutex) -{ - ObtainSemaphore(mutex); - - return true; -} - -bool -of_mutex_trylock(of_mutex_t *mutex) -{ - if (!AttemptSemaphore(mutex)) { - errno = EBUSY; - return false; - } - - return true; -} - -bool -of_mutex_unlock(of_mutex_t *mutex) -{ - ReleaseSemaphore(mutex); - - return true; -} - -bool -of_mutex_free(of_mutex_t *mutex) -{ - return true; -} - -bool -of_rmutex_new(of_rmutex_t *rmutex) -{ - return of_mutex_new(rmutex); -} - -bool -of_rmutex_lock(of_rmutex_t *rmutex) -{ - return of_mutex_lock(rmutex); -} - -bool -of_rmutex_trylock(of_rmutex_t *rmutex) -{ - return of_mutex_trylock(rmutex); -} - -bool -of_rmutex_unlock(of_rmutex_t *rmutex) -{ - return of_mutex_unlock(rmutex); -} - -bool -of_rmutex_free(of_rmutex_t *rmutex) -{ - return of_mutex_free(rmutex); -} DELETED src/mutex_pthread.m Index: src/mutex_pthread.m ================================================================== --- src/mutex_pthread.m +++ src/mutex_pthread.m @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -bool -of_mutex_new(of_mutex_t *mutex) -{ - return (pthread_mutex_init(mutex, NULL) == 0); -} - -bool -of_mutex_lock(of_mutex_t *mutex) -{ - return (pthread_mutex_lock(mutex) == 0); -} - -bool -of_mutex_trylock(of_mutex_t *mutex) -{ - return (pthread_mutex_trylock(mutex) == 0); -} - -bool -of_mutex_unlock(of_mutex_t *mutex) -{ - return (pthread_mutex_unlock(mutex) == 0); -} - -bool -of_mutex_free(of_mutex_t *mutex) -{ - return (pthread_mutex_destroy(mutex) == 0); -} - -#ifdef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES -bool -of_rmutex_new(of_rmutex_t *rmutex) -{ - pthread_mutexattr_t attr; - - if (pthread_mutexattr_init(&attr) != 0) - return false; - - if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) - return false; - - if (pthread_mutex_init(rmutex, &attr) != 0) - return false; - - if (pthread_mutexattr_destroy(&attr) != 0) - return false; - - return true; -} - -bool -of_rmutex_lock(of_rmutex_t *rmutex) -{ - return of_mutex_lock(rmutex); -} - -bool -of_rmutex_trylock(of_rmutex_t *rmutex) -{ - return of_mutex_trylock(rmutex); -} - -bool -of_rmutex_unlock(of_rmutex_t *rmutex) -{ - return of_mutex_unlock(rmutex); -} - -bool -of_rmutex_free(of_rmutex_t *rmutex) -{ - return of_mutex_free(rmutex); -} -#endif DELETED src/mutex_winapi.m Index: src/mutex_winapi.m ================================================================== --- src/mutex_winapi.m +++ src/mutex_winapi.m @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include - -bool -of_mutex_new(of_mutex_t *mutex) -{ - InitializeCriticalSection(mutex); - - return true; -} - -bool -of_mutex_lock(of_mutex_t *mutex) -{ - EnterCriticalSection(mutex); - - return true; -} - -bool -of_mutex_trylock(of_mutex_t *mutex) -{ - if (!TryEnterCriticalSection(mutex)) { - errno = EBUSY; - return false; - } - - return true; -} - -bool -of_mutex_unlock(of_mutex_t *mutex) -{ - LeaveCriticalSection(mutex); - - return true; -} - -bool -of_mutex_free(of_mutex_t *mutex) -{ - DeleteCriticalSection(mutex); - - return true; -} - -bool -of_rmutex_new(of_rmutex_t *rmutex) -{ - return of_mutex_new(rmutex); -} - -bool -of_rmutex_lock(of_rmutex_t *rmutex) -{ - return of_mutex_lock(rmutex); -} - -bool -of_rmutex_trylock(of_rmutex_t *rmutex) -{ - return of_mutex_trylock(rmutex); -} - -bool -of_rmutex_unlock(of_rmutex_t *rmutex) -{ - return of_mutex_unlock(rmutex); -} - -bool -of_rmutex_free(of_rmutex_t *rmutex) -{ - return of_mutex_free(rmutex); -} ADDED src/platform/amiga/OFString+PathAdditions.m Index: src/platform/amiga/OFString+PathAdditions.m ================================================================== --- src/platform/amiga/OFString+PathAdditions.m +++ src/platform/amiga/OFString+PathAdditions.m @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFString+PathAdditions.h" +#import "OFArray.h" + +#import "OFOutOfRangeException.h" + +int _OFString_PathAdditions_reference; + +@implementation OFString (PathAdditions) ++ (OFString *)pathWithComponents: (OFArray *)components +{ + OFMutableString *ret = [OFMutableString string]; + void *pool = objc_autoreleasePoolPush(); + bool firstAfterDevice = true; + + for (OFString *component in components) { + if (component.length == 0) + continue; + + if (!firstAfterDevice) + [ret appendString: @"/"]; + + [ret appendString: component]; + + if (![component hasSuffix: @":"]) + firstAfterDevice = false; + } + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)isAbsolutePath +{ + return [self containsString: @":"]; +} + +- (OFArray *)pathComponents +{ + OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + const char *cString = self.UTF8String; + size_t i, last = 0, cStringLength = self.UTF8StringLength; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return ret; + } + + for (i = 0; i < cStringLength; i++) { + if (cString[i] == '/') { + if (i - last != 0) + [ret addObject: [OFString + stringWithUTF8String: cString + last + length: i - last]]; + else + [ret addObject: @"/"]; + + last = i + 1; + } else if (cString[i] == ':') { + [ret addObject: [OFString + stringWithUTF8String: cString + last + length: i - last + 1]]; + + last = i + 1; + } + } + if (i - last != 0) + [ret addObject: [OFString stringWithUTF8String: cString + last + length: i - last]]; + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (OFString *)lastPathComponent +{ + /* + * AmigaOS needs the full parsing to determine the last path component. + * This could be optimized by not creating the temporary objects, + * though. + */ + void *pool = objc_autoreleasePoolPush(); + OFString *ret = self.pathComponents.lastObject; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)pathExtension +{ + void *pool = objc_autoreleasePoolPush(); + OFString *ret, *fileName; + size_t pos; + + fileName = self.lastPathComponent; + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + ret = [fileName substringWithRange: + of_range(pos + 1, fileName.length - pos - 1)]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByDeletingLastPathComponent +{ + /* + * AmigaOS needs the full parsing to delete the last path component. + * This could be optimized, though. + */ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components = self.pathComponents; + size_t count = components.count; + OFString *ret; + + if (count < 2) { + if ([components.firstObject hasSuffix: @":"]) { + ret = [components.firstObject retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; + } + + objc_autoreleasePoolPop(pool); + return @""; + } + + components = [components objectsInRange: + of_range(0, components.count - 1)]; + ret = [OFString pathWithComponents: components]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByDeletingPathExtension +{ + void *pool; + OFMutableArray OF_GENERIC(OFString *) *components; + OFString *ret, *fileName; + size_t pos; + + if (self.length == 0) + return [[self copy] autorelease]; + + pool = objc_autoreleasePoolPush(); + components = [[self.pathComponents mutableCopy] autorelease]; + fileName = components.lastObject; + + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + fileName = [fileName substringWithRange: of_range(0, pos)]; + [components replaceObjectAtIndex: components.count - 1 + withObject: fileName]; + + ret = [OFString pathWithComponents: components]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByStandardizingPath +{ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components; + OFMutableArray OF_GENERIC(OFString *) *array; + OFString *ret; + bool done = false; + + if (self.length == 0) + return @""; + + components = self.pathComponents; + + if (components.count == 1) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + array = [[components mutableCopy] autorelease]; + + while (!done) { + size_t length = array.count; + + done = true; + + for (size_t i = 0; i < length; i++) { + OFString *component = [array objectAtIndex: i]; + OFString *parent = + (i > 0 ? [array objectAtIndex: i - 1] : 0); + + if (component.length == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([component isEqual: @"/"] && + parent != nil && ![parent isEqual: @"/"]) { + [array removeObjectsInRange: + of_range(i - 1, 2)]; + + done = false; + break; + } + } + } + + ret = [OFString pathWithComponents: array]; + + if ([self hasSuffix: @"/"]) + ret = [ret stringByAppendingString: @"/"]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByAppendingPathComponent: (OFString *)component +{ + if ([self hasSuffix: @"/"] || [self hasSuffix: @":"]) + return [self stringByAppendingString: component]; + else { + OFMutableString *ret = [[self mutableCopy] autorelease]; + + [ret appendString: @"/"]; + [ret appendString: component]; + + [ret makeImmutable]; + + return ret; + } +} +@end ADDED src/platform/amiga/condition.m Index: src/platform/amiga/condition.m ================================================================== --- src/platform/amiga/condition.m +++ src/platform/amiga/condition.m @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#import "condition.h" + +#include +#include +#ifndef OF_AMIGAOS4 +# include +#endif + +bool +of_condition_new(of_condition_t *condition) +{ + condition->waitingTasks = NULL; + + return true; +} + +bool +of_condition_signal(of_condition_t *condition) +{ + Forbid(); + @try { + if (condition->waitingTasks == NULL) + return true; + + Signal(condition->waitingTasks->task, + (1ul << condition->waitingTasks->sigBit)); + + condition->waitingTasks = condition->waitingTasks->next; + } @finally { + Permit(); + } + + return true; +} + +bool +of_condition_broadcast(of_condition_t *condition) +{ + Forbid(); + @try { + if (condition->waitingTasks == NULL) + return true; + + while (condition->waitingTasks != NULL) { + Signal(condition->waitingTasks->task, + (1ul << condition->waitingTasks->sigBit)); + + condition->waitingTasks = condition->waitingTasks->next; + } + } @finally { + Permit(); + } + + return true; +} + +bool +of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) +{ + ULONG signalMask = 0; + + return of_condition_wait_or_signal(condition, mutex, &signalMask); +} + +bool +of_condition_wait_or_signal(of_condition_t *condition, of_mutex_t *mutex, + ULONG *signalMask) +{ + struct of_condition_waiting_task waitingTask = { + .task = FindTask(NULL), + .sigBit = AllocSignal(-1) + }; + bool ret; + ULONG mask; + + if (waitingTask.sigBit == -1) { + errno = EAGAIN; + return false; + } + + Forbid(); + + if (!of_mutex_unlock(mutex)) { + FreeSignal(waitingTask.sigBit); + return false; + } + + waitingTask.next = condition->waitingTasks; + condition->waitingTasks = &waitingTask; + + mask = Wait((1ul << waitingTask.sigBit) | *signalMask); + if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask)) + ret = of_mutex_lock(mutex); + else { + /* + * This should not happen - it means something interrupted the + * Wait(), so the best we can do is return EINTR. + */ + ret = false; + errno = EINTR; + } + + FreeSignal(waitingTask.sigBit); + + Permit(); + + return ret; +} + +bool +of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, + of_time_interval_t timeout) +{ + ULONG signalMask = 0; + + return of_condition_timed_wait_or_signal(condition, mutex, timeout, + &signalMask); +} + +bool +of_condition_timed_wait_or_signal(of_condition_t *condition, of_mutex_t *mutex, + of_time_interval_t timeout, ULONG *signalMask) +{ + struct of_condition_waiting_task waitingTask = { + .task = FindTask(NULL), + .sigBit = AllocSignal(-1) + }; + struct MsgPort port = { + .mp_Node = { + .ln_Type = NT_MSGPORT + }, + .mp_Flags = PA_SIGNAL, + .mp_SigTask = waitingTask.task, + .mp_SigBit = AllocSignal(-1) + }; +#ifdef OF_AMIGAOS4 + struct TimeRequest request = { + .Request = { +#else + struct timerequest request = { + .tr_node = { +#endif + .io_Message = { + .mn_Node = { + .ln_Type = NT_MESSAGE + }, + .mn_ReplyPort = &port, + .mn_Length = sizeof(request) + }, + .io_Command = TR_ADDREQUEST + }, +#ifdef OF_AMIGAOS4 + .Time = { + .Seconds = (ULONG)timeout, + .Microseconds = + (timeout - request.Time.Seconds) * 1000000 +#else + .tr_time = { + .tv_sec = (ULONG)timeout, + .tv_micro = (timeout - request.tr_time.tv_sec) * 1000000 +#endif + } + }; + ULONG mask; + bool ret; + + NewList(&port.mp_MsgList); + + if (waitingTask.sigBit == -1 || port.mp_SigBit == -1) { + errno = EAGAIN; + goto fail; + } + + if (OpenDevice("timer.device", UNIT_MICROHZ, + (struct IORequest *)&request, 0) != 0) { + errno = EAGAIN; + goto fail; + } + + Forbid(); + + if (!of_mutex_unlock(mutex)) { + Permit(); + goto fail; + } + + waitingTask.next = condition->waitingTasks; + condition->waitingTasks = &waitingTask; + + SendIO((struct IORequest *)&request); + + mask = Wait((1ul << waitingTask.sigBit) | (1ul << port.mp_SigBit) | + *signalMask); + if (mask & (1ul << waitingTask.sigBit) || (*signalMask &= mask)) + ret = of_mutex_lock(mutex); + else if (mask & (1ul << port.mp_SigBit)) { + ret = false; + errno = ETIMEDOUT; + } else { + /* + * This should not happen - it means something interrupted the + * Wait(), so the best we can do is return EINTR. + */ + ret = false; + errno = EINTR; + } + + condition->waitingTasks = waitingTask.next; + + if (!CheckIO((struct IORequest *)&request)) { + AbortIO((struct IORequest *)&request); + WaitIO((struct IORequest *)&request); + } + CloseDevice((struct IORequest *)&request); + + Permit(); + + FreeSignal(waitingTask.sigBit); + FreeSignal(port.mp_SigBit); + + return ret; + +fail: + if (waitingTask.sigBit != -1) + FreeSignal(waitingTask.sigBit); + if (port.mp_SigBit != -1) + FreeSignal(port.mp_SigBit); + + return false; +} + +bool +of_condition_free(of_condition_t *condition) +{ + Forbid(); + @try { + if (condition->waitingTasks != NULL) { + errno = EBUSY; + return false; + } + } @finally { + Permit(); + } + + return true; +} ADDED src/platform/amiga/mutex.m Index: src/platform/amiga/mutex.m ================================================================== --- src/platform/amiga/mutex.m +++ src/platform/amiga/mutex.m @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#import "mutex.h" + +#include + +bool +of_mutex_new(of_mutex_t *mutex) +{ + InitSemaphore(mutex); + + return true; +} + +bool +of_mutex_lock(of_mutex_t *mutex) +{ + ObtainSemaphore(mutex); + + return true; +} + +bool +of_mutex_trylock(of_mutex_t *mutex) +{ + if (!AttemptSemaphore(mutex)) { + errno = EBUSY; + return false; + } + + return true; +} + +bool +of_mutex_unlock(of_mutex_t *mutex) +{ + ReleaseSemaphore(mutex); + + return true; +} + +bool +of_mutex_free(of_mutex_t *mutex) +{ + return true; +} + +bool +of_rmutex_new(of_rmutex_t *rmutex) +{ + return of_mutex_new(rmutex); +} + +bool +of_rmutex_lock(of_rmutex_t *rmutex) +{ + return of_mutex_lock(rmutex); +} + +bool +of_rmutex_trylock(of_rmutex_t *rmutex) +{ + return of_mutex_trylock(rmutex); +} + +bool +of_rmutex_unlock(of_rmutex_t *rmutex) +{ + return of_mutex_unlock(rmutex); +} + +bool +of_rmutex_free(of_rmutex_t *rmutex) +{ + return of_mutex_free(rmutex); +} ADDED src/platform/amiga/thread.m Index: src/platform/amiga/thread.m ================================================================== --- src/platform/amiga/thread.m +++ src/platform/amiga/thread.m @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include +#include + +#import "OFData.h" + +#import "thread.h" +#import "tlskey.h" + +#include +#include +#include + +extern void of_tlskey_thread_exited(void); +static of_tlskey_t threadKey; + +OF_CONSTRUCTOR() +{ + OF_ENSURE(of_tlskey_new(&threadKey)); +} + +static void +functionWrapper(void) +{ + bool detached = false; + of_thread_t thread = + (of_thread_t)((struct Process *)FindTask(NULL))->pr_ExitData; + OF_ENSURE(of_tlskey_set(threadKey, thread)); + + thread->function(thread->object); + + ObtainSemaphore(&thread->semaphore); + @try { + thread->done = true; + + of_tlskey_thread_exited(); + + if (thread->detached) + detached = true; + else if (thread->joinTask != NULL) + Signal(thread->joinTask, (1ul << thread->joinSigBit)); + } @finally { + ReleaseSemaphore(&thread->semaphore); + } + + if (detached) + free(thread); +} + +bool +of_thread_attr_init(of_thread_attr_t *attr) +{ + attr->priority = 0; + attr->stackSize = 0; + + return true; +} + +bool +of_thread_new(of_thread_t *thread, const char *name, void (*function)(id), + id object, const of_thread_attr_t *attr) +{ + OFMutableData *tags = nil; + + if ((*thread = calloc(1, sizeof(**thread))) == NULL) { + errno = ENOMEM; + return false; + } + + @try { + (*thread)->function = function; + (*thread)->object = object; + InitSemaphore(&(*thread)->semaphore); + + tags = [[OFMutableData alloc] + initWithItemSize: sizeof(struct TagItem) + capacity: 12]; +#define ADD_TAG(tag, data) \ + { \ + struct TagItem t = { \ + .ti_Tag = tag, \ + .ti_Data = data \ + }; \ + [tags addItem: &t]; \ + } + ADD_TAG(NP_Entry, (ULONG)functionWrapper) + ADD_TAG(NP_ExitData, (ULONG)*thread) +#ifdef OF_AMIGAOS4 + ADD_TAG(NP_Child, TRUE) +#endif +#ifdef OF_MORPHOS + ADD_TAG(NP_CodeType, CODETYPE_PPC); +#endif + if (name != NULL) + ADD_TAG(NP_Name, (ULONG)name); + + ADD_TAG(NP_Input, ((struct Process *)FindTask(NULL))->pr_CIS) + ADD_TAG(NP_Output, ((struct Process *)FindTask(NULL))->pr_COS) + ADD_TAG(NP_Error, ((struct Process *)FindTask(NULL))->pr_CES) + ADD_TAG(NP_CloseInput, FALSE) + ADD_TAG(NP_CloseOutput, FALSE) + ADD_TAG(NP_CloseError, FALSE) + + if (attr != NULL && attr->priority != 0) { + if (attr->priority < 1 || attr->priority > 1) { + errno = EINVAL; + return false; + } + + /* + * -1 should be -128 (lowest possible priority) while + * +1 should be +127 (highest possible priority). + */ + ADD_TAG(NP_Priority, (attr->priority > 0 + ? attr->priority * 127 : attr->priority * 128)) + } + + if (attr != NULL && attr->stackSize != 0) + ADD_TAG(NP_StackSize, attr->stackSize) + else + ADD_TAG(NP_StackSize, + ((struct Process *)FindTask(NULL))->pr_StackSize) + + ADD_TAG(TAG_DONE, 0) +#undef ADD_TAG + + (*thread)->task = (struct Task *)CreateNewProc(tags.items); + if ((*thread)->task == NULL) { + free(*thread); + errno = EAGAIN; + return false; + } + } @catch (id e) { + free(*thread); + @throw e; + } @finally { + [tags release]; + } + + return true; +} + +of_thread_t +of_thread_current(void) +{ + return of_tlskey_get(threadKey); +} + +bool +of_thread_join(of_thread_t thread) +{ + ObtainSemaphore(&thread->semaphore); + @try { + if (thread->done) { + free(thread); + return true; + } + + if (thread->detached || thread->joinTask != NULL) { + errno = EINVAL; + return false; + } + + if ((thread->joinSigBit = AllocSignal(-1)) == -1) { + errno = EAGAIN; + return false; + } + + thread->joinTask = FindTask(NULL); + } @finally { + ReleaseSemaphore(&thread->semaphore); + } + + Wait(1ul << thread->joinSigBit); + FreeSignal(thread->joinSigBit); + + assert(thread->done); + free(thread); + + return true; +} + +bool +of_thread_detach(of_thread_t thread) +{ + ObtainSemaphore(&thread->semaphore); + + if (thread->done) + free(thread); + else + thread->detached = true; + + ReleaseSemaphore(&thread->semaphore); + + return true; +} + +void +of_thread_set_name(const char *name) +{ +} ADDED src/platform/amiga/tlskey.m Index: src/platform/amiga/tlskey.m ================================================================== --- src/platform/amiga/tlskey.m +++ src/platform/amiga/tlskey.m @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "tlskey.h" + +#include +#include + +/* + * As we use this file in both the runtime and ObjFW, and since AmigaOS always + * has the runtime, use the hashtable from the runtime. + */ +#import "runtime/private.h" + +static of_tlskey_t firstKey = NULL, lastKey = NULL; +static struct SignalSemaphore semaphore; +static bool semaphoreInitialized = false; + +static uint32_t +hashFunc(const void *ptr) +{ + return (uint32_t)(uintptr_t)ptr; +} + +static bool +equalFunc(const void *ptr1, const void *ptr2) +{ + return (ptr1 == ptr2); +} + +OF_CONSTRUCTOR() +{ + if (!semaphoreInitialized) { + InitSemaphore(&semaphore); + semaphoreInitialized = true; + } +} + +bool +of_tlskey_new(of_tlskey_t *key) +{ + if (!semaphoreInitialized) { + /* + * We might be called from another constructor, while ours has + * not run yet. This is safe, as the constructor is definitely + * run before a thread is spawned. + */ + InitSemaphore(&semaphore); + semaphoreInitialized = true; + } + + if ((*key = malloc(sizeof(**key))) == NULL) + return false; + + (*key)->table = NULL; + + ObtainSemaphore(&semaphore); + @try { + (*key)->next = NULL; + (*key)->previous = lastKey; + + if (lastKey != NULL) + lastKey->next = *key; + + lastKey = *key; + + if (firstKey == NULL) + firstKey = *key; + } @finally { + ReleaseSemaphore(&semaphore); + } + + /* We create the hash table lazily. */ + return true; +} + +bool +of_tlskey_free(of_tlskey_t key) +{ + ObtainSemaphore(&semaphore); + @try { + if (key->previous != NULL) + key->previous->next = key->next; + if (key->next != NULL) + key->next->previous = key->previous; + + if (firstKey == key) + firstKey = key->next; + if (lastKey == key) + lastKey = key->previous; + + objc_hashtable_free(key->table); + free(key); + } @finally { + ReleaseSemaphore(&semaphore); + } + + return true; +} + +void * +of_tlskey_get(of_tlskey_t key) +{ + void *ret; + + ObtainSemaphore(&semaphore); + @try { + if (key->table == NULL) + return NULL; + + ret = objc_hashtable_get(key->table, FindTask(NULL)); + } @finally { + ReleaseSemaphore(&semaphore); + } + + return ret; +} + +bool +of_tlskey_set(of_tlskey_t key, void *ptr) +{ + ObtainSemaphore(&semaphore); + @try { + struct Task *task = FindTask(NULL); + + if (key->table == NULL) + key->table = objc_hashtable_new(hashFunc, equalFunc, 2); + + if (ptr == NULL) + objc_hashtable_delete(key->table, task); + else + objc_hashtable_set(key->table, task, ptr); + } @catch (id e) { + return false; + } @finally { + ReleaseSemaphore(&semaphore); + } + + return true; +} + +void +of_tlskey_thread_exited(void) +{ + ObtainSemaphore(&semaphore); + @try { + struct Task *task = FindTask(NULL); + + for (of_tlskey_t iter = firstKey; iter != NULL; + iter = iter->next) + if (iter->table != NULL) + objc_hashtable_delete(iter->table, task); + } @finally { + ReleaseSemaphore(&semaphore); + } +} ADDED src/platform/libfat/OFString+PathAdditions.m Index: src/platform/libfat/OFString+PathAdditions.m ================================================================== --- src/platform/libfat/OFString+PathAdditions.m +++ src/platform/libfat/OFString+PathAdditions.m @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFString+PathAdditions.h" +#import "OFArray.h" + +#import "OFOutOfRangeException.h" + +int _OFString_PathAdditions_reference; + +@implementation OFString (PathAdditions) ++ (OFString *)pathWithComponents: (OFArray *)components +{ + OFMutableString *ret = [OFMutableString string]; + void *pool = objc_autoreleasePoolPush(); + bool first = true; + + for (OFString *component in components) { + if (component.length == 0) + continue; + + if ([component isEqual: @"/"]) + continue; + + if (!first && ![ret hasSuffix: @"/"]) + [ret appendString: @"/"]; + + [ret appendString: component]; + + first = false; + } + + if ([ret hasSuffix: @":"]) + [ret appendString: @"/"]; + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)isAbsolutePath +{ + return [self containsString: @":/"]; +} + +- (OFArray *)pathComponents +{ + OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + const char *cString = self.UTF8String; + size_t i, last = 0, cStringLength = self.UTF8StringLength; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return ret; + } + + for (i = 0; i < cStringLength; i++) { + if (cString[i] == '/') { + if (i - last != 0) + [ret addObject: [OFString + stringWithUTF8String: cString + last + length: i - last]]; + + last = i + 1; + } + } + if (i - last != 0) + [ret addObject: [OFString stringWithUTF8String: cString + last + length: i - last]]; + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (OFString *)lastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + const char *cString; + size_t cStringLength; + ssize_t i; + OFString *ret; + + if ([self hasSuffix: @":/"]) + return self; + + cString = self.UTF8String; + cStringLength = self.UTF8StringLength; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (cString[cStringLength - 1] == '/') + cStringLength--; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (cStringLength - 1 > SSIZE_MAX) + @throw [OFOutOfRangeException exception]; + + for (i = cStringLength - 1; i >= 0; i--) { + if (cString[i] == '/') { + i++; + break; + } + } + + /* + * Only one component, but the trailing delimiter might have been + * removed, so return a new string anyway. + */ + if (i < 0) + i = 0; + + ret = [[OFString alloc] initWithUTF8String: cString + i + length: cStringLength - i]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)pathExtension +{ + void *pool = objc_autoreleasePoolPush(); + OFString *ret, *fileName; + size_t pos; + + fileName = self.lastPathComponent; + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + ret = [fileName substringWithRange: + of_range(pos + 1, fileName.length - pos - 1)]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByDeletingLastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + const char *cString; + size_t cStringLength; + OFString *ret; + + if ([self hasSuffix: @":/"]) + return self; + + cString = self.UTF8String; + cStringLength = self.UTF8StringLength; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (cString[cStringLength - 1] == '/') + cStringLength--; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + for (size_t i = cStringLength; i >= 1; i--) { + if (cString[i - 1] == '/') { + ret = [[OFString alloc] initWithUTF8String: cString + length: i - 1]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; + } + } + + objc_autoreleasePoolPop(pool); + + return @"."; +} + +- (OFString *)stringByDeletingPathExtension +{ + void *pool; + OFMutableArray OF_GENERIC(OFString *) *components; + OFString *ret, *fileName; + size_t pos; + + if (self.length == 0) + return [[self copy] autorelease]; + + pool = objc_autoreleasePoolPush(); + components = [[self.pathComponents mutableCopy] autorelease]; + fileName = components.lastObject; + + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + fileName = [fileName substringWithRange: of_range(0, pos)]; + [components replaceObjectAtIndex: components.count - 1 + withObject: fileName]; + + ret = [OFString pathWithComponents: components]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByStandardizingPath +{ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components; + OFMutableArray OF_GENERIC(OFString *) *array; + OFString *ret; + bool done = false; + + if (self.length == 0) + return @""; + + components = self.pathComponents; + + if (components.count == 1) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + array = [[components mutableCopy] autorelease]; + + while (!done) { + size_t length = array.count; + + done = true; + + for (size_t i = 0; i < length; i++) { + OFString *component = [array objectAtIndex: i]; + OFString *parent = + (i > 0 ? [array objectAtIndex: i - 1] : 0); + + if ([component isEqual: @"."] || + component.length == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([component isEqual: @".."] && + parent != nil && ![parent isEqual: @".."]) { + [array removeObjectsInRange: + of_range(i - 1, 2)]; + + done = false; + break; + } + } + } + + if ([self hasSuffix: @"/"]) + [array addObject: @""]; + + ret = [[array componentsJoinedByString: @"/"] retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)stringByAppendingPathComponent: (OFString *)component +{ + if ([self hasSuffix: @"/"]) + return [self stringByAppendingString: component]; + else { + OFMutableString *ret = [[self mutableCopy] autorelease]; + + [ret appendString: @"/"]; + [ret appendString: component]; + + [ret makeImmutable]; + + return ret; + } +} +@end ADDED src/platform/posix/OFString+PathAdditions.m Index: src/platform/posix/OFString+PathAdditions.m ================================================================== --- src/platform/posix/OFString+PathAdditions.m +++ src/platform/posix/OFString+PathAdditions.m @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFString+PathAdditions.h" +#import "OFArray.h" + +#import "OFOutOfRangeException.h" + +int _OFString_PathAdditions_reference; + +@implementation OFString (PathAdditions) ++ (OFString *)pathWithComponents: (OFArray *)components +{ + OFMutableString *ret = [OFMutableString string]; + void *pool = objc_autoreleasePoolPush(); + bool first = true; + + for (OFString *component in components) { + if (component.length == 0) + continue; + + if (!first && [component isEqual: @"/"]) + continue; + + if (!first && ![ret hasSuffix: @"/"]) + [ret appendString: @"/"]; + + [ret appendString: component]; + + first = false; + } + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)isAbsolutePath +{ + return [self hasPrefix: @"/"]; +} + +- (OFArray *)pathComponents +{ + OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + const char *cString = self.UTF8String; + size_t i, last = 0, cStringLength = self.UTF8StringLength; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return ret; + } + + for (i = 0; i < cStringLength; i++) { + if (cString[i] == '/') { + if (i == 0) + [ret addObject: @"/"]; + else if (i - last != 0) + [ret addObject: [OFString + stringWithUTF8String: cString + last + length: i - last]]; + + last = i + 1; + } + } + if (i - last != 0) + [ret addObject: [OFString stringWithUTF8String: cString + last + length: i - last]]; + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (OFString *)lastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + const char *cString = self.UTF8String; + size_t cStringLength = self.UTF8StringLength; + ssize_t i; + OFString *ret; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (cString[cStringLength - 1] == '/') + cStringLength--; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @"/"; + } + + if (cStringLength - 1 > SSIZE_MAX) + @throw [OFOutOfRangeException exception]; + + for (i = cStringLength - 1; i >= 0; i--) { + if (cString[i] == '/') { + i++; + break; + } + } + + /* + * Only one component, but the trailing delimiter might have been + * removed, so return a new string anyway. + */ + if (i < 0) + i = 0; + + ret = [[OFString alloc] initWithUTF8String: cString + i + length: cStringLength - i]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)pathExtension +{ + void *pool = objc_autoreleasePoolPush(); + OFString *ret, *fileName; + size_t pos; + + fileName = self.lastPathComponent; + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + ret = [fileName substringWithRange: + of_range(pos + 1, fileName.length - pos - 1)]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByDeletingLastPathComponent +{ + void *pool = objc_autoreleasePoolPush(); + const char *cString = self.UTF8String; + size_t cStringLength = self.UTF8StringLength; + OFString *ret; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (cString[cStringLength - 1] == '/') + cStringLength--; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return @"/"; + } + + for (size_t i = cStringLength; i >= 1; i--) { + if (cString[i - 1] == '/') { + if (i == 1) { + objc_autoreleasePoolPop(pool); + return @"/"; + } + + ret = [[OFString alloc] initWithUTF8String: cString + length: i - 1]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; + } + } + + objc_autoreleasePoolPop(pool); + + return @"."; +} + +- (OFString *)stringByDeletingPathExtension +{ + void *pool; + OFMutableArray OF_GENERIC(OFString *) *components; + OFString *ret, *fileName; + size_t pos; + + if (self.length == 0) + return [[self copy] autorelease]; + + pool = objc_autoreleasePoolPush(); + components = [[self.pathComponents mutableCopy] autorelease]; + fileName = components.lastObject; + + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + fileName = [fileName substringWithRange: of_range(0, pos)]; + [components replaceObjectAtIndex: [components count] - 1 + withObject: fileName]; + + ret = [OFString pathWithComponents: components]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByStandardizingPath +{ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components; + OFMutableArray OF_GENERIC(OFString *) *array; + OFString *ret; + bool done = false, startsWithSlash; + + if (self.length == 0) + return @""; + + components = self.pathComponents; + + if (components.count == 1) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + array = [[components mutableCopy] autorelease]; + startsWithSlash = [self hasPrefix: @"/"]; + + if (startsWithSlash) + [array removeObjectAtIndex: 0]; + + while (!done) { + size_t length = array.count; + + done = true; + + for (size_t i = 0; i < length; i++) { + OFString *component = [array objectAtIndex: i]; + OFString *parent = + (i > 0 ? [array objectAtIndex: i - 1] : 0); + + if ([component isEqual: @"."] || + component.length == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([component isEqual: @".."] && + parent != nil && ![parent isEqual: @".."]) { + [array removeObjectsInRange: + of_range(i - 1, 2)]; + + done = false; + break; + } + } + } + + if (startsWithSlash) + [array insertObject: @"" + atIndex: 0]; + + if ([self hasSuffix: @"/"]) + [array addObject: @""]; + + ret = [[array componentsJoinedByString: @"/"] retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)stringByAppendingPathComponent: (OFString *)component +{ + if ([self hasSuffix: @"/"]) + return [self stringByAppendingString: component]; + else { + OFMutableString *ret = [[self mutableCopy] autorelease]; + + [ret appendString: @"/"]; + [ret appendString: component]; + + [ret makeImmutable]; + + return ret; + } +} +@end ADDED src/platform/posix/condition.m Index: src/platform/posix/condition.m ================================================================== --- src/platform/posix/condition.m +++ src/platform/posix/condition.m @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "condition.h" + +bool +of_condition_new(of_condition_t *condition) +{ + return (pthread_cond_init(condition, NULL) == 0); +} + +bool +of_condition_signal(of_condition_t *condition) +{ + return (pthread_cond_signal(condition) == 0); +} + +bool +of_condition_broadcast(of_condition_t *condition) +{ + return (pthread_cond_broadcast(condition) == 0); +} + +bool +of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) +{ + return (pthread_cond_wait(condition, mutex) == 0); +} + +bool +of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, + of_time_interval_t timeout) +{ + struct timespec ts; + + ts.tv_sec = (time_t)timeout; + ts.tv_nsec = (timeout - ts.tv_sec) * 1000000000; + + return (pthread_cond_timedwait(condition, mutex, &ts) == 0); +} + +bool +of_condition_free(of_condition_t *condition) +{ + return (pthread_cond_destroy(condition) == 0); +} ADDED src/platform/posix/mutex.m Index: src/platform/posix/mutex.m ================================================================== --- src/platform/posix/mutex.m +++ src/platform/posix/mutex.m @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "mutex.h" + +bool +of_mutex_new(of_mutex_t *mutex) +{ + return (pthread_mutex_init(mutex, NULL) == 0); +} + +bool +of_mutex_lock(of_mutex_t *mutex) +{ + return (pthread_mutex_lock(mutex) == 0); +} + +bool +of_mutex_trylock(of_mutex_t *mutex) +{ + return (pthread_mutex_trylock(mutex) == 0); +} + +bool +of_mutex_unlock(of_mutex_t *mutex) +{ + return (pthread_mutex_unlock(mutex) == 0); +} + +bool +of_mutex_free(of_mutex_t *mutex) +{ + return (pthread_mutex_destroy(mutex) == 0); +} + +#ifdef OF_HAVE_RECURSIVE_PTHREAD_MUTEXES +bool +of_rmutex_new(of_rmutex_t *rmutex) +{ + pthread_mutexattr_t attr; + + if (pthread_mutexattr_init(&attr) != 0) + return false; + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) + return false; + + if (pthread_mutex_init(rmutex, &attr) != 0) + return false; + + if (pthread_mutexattr_destroy(&attr) != 0) + return false; + + return true; +} + +bool +of_rmutex_lock(of_rmutex_t *rmutex) +{ + return of_mutex_lock(rmutex); +} + +bool +of_rmutex_trylock(of_rmutex_t *rmutex) +{ + return of_mutex_trylock(rmutex); +} + +bool +of_rmutex_unlock(of_rmutex_t *rmutex) +{ + return of_mutex_unlock(rmutex); +} + +bool +of_rmutex_free(of_rmutex_t *rmutex) +{ + return of_mutex_free(rmutex); +} +#else +bool +of_rmutex_new(of_rmutex_t *rmutex) +{ + if (!of_mutex_new(&rmutex->mutex)) + return false; + + if (!of_tlskey_new(&rmutex->count)) + return false; + + return true; +} + +bool +of_rmutex_lock(of_rmutex_t *rmutex) +{ + uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); + + if (count > 0) { + if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) + return false; + + return true; + } + + if (!of_mutex_lock(&rmutex->mutex)) + return false; + + if (!of_tlskey_set(rmutex->count, (void *)1)) { + of_mutex_unlock(&rmutex->mutex); + return false; + } + + return true; +} + +bool +of_rmutex_trylock(of_rmutex_t *rmutex) +{ + uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); + + if (count > 0) { + if (!of_tlskey_set(rmutex->count, (void *)(count + 1))) + return false; + + return true; + } + + if (!of_mutex_trylock(&rmutex->mutex)) + return false; + + if (!of_tlskey_set(rmutex->count, (void *)1)) { + of_mutex_unlock(&rmutex->mutex); + return false; + } + + return true; +} + +bool +of_rmutex_unlock(of_rmutex_t *rmutex) +{ + uintptr_t count = (uintptr_t)of_tlskey_get(rmutex->count); + + if (count > 1) { + if (!of_tlskey_set(rmutex->count, (void *)(count - 1))) + return false; + + return true; + } + + if (!of_tlskey_set(rmutex->count, (void *)0)) + return false; + + if (!of_mutex_unlock(&rmutex->mutex)) + return false; + + return true; +} + +bool +of_rmutex_free(of_rmutex_t *rmutex) +{ + if (!of_mutex_free(&rmutex->mutex)) + return false; + + if (!of_tlskey_free(rmutex->count)) + return false; + + return true; +} +#endif ADDED src/platform/posix/thread.m Index: src/platform/posix/thread.m ================================================================== --- src/platform/posix/thread.m +++ src/platform/posix/thread.m @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#ifdef HAVE_PTHREAD_NP_H +# include +#endif + +#ifdef OF_HAIKU +# include +#endif + +#import "thread.h" +#import "macros.h" + +static int minPrio = 0, maxPrio = 0, normalPrio = 0; + +struct thread_ctx { + void (*function)(id object); + id object; + const char *name; +}; + +/* + * This is done here to make sure this is done as early as possible in the main + * thread. + */ +OF_CONSTRUCTOR() +{ + pthread_attr_t pattr; + + if (pthread_attr_init(&pattr) == 0) { +#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY + int policy; +#endif + struct sched_param param; + +#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY + if (pthread_attr_getschedpolicy(&pattr, &policy) == 0) { + minPrio = sched_get_priority_min(policy); + maxPrio = sched_get_priority_max(policy); + + if (minPrio == -1 || maxPrio == -1) + minPrio = maxPrio = 0; + } + + if (pthread_attr_getschedparam(&pattr, ¶m) != 0) + normalPrio = param.sched_priority; + else + minPrio = maxPrio = 0; + + pthread_attr_destroy(&pattr); +#endif + } +} + +static void * +functionWrapper(void *data) +{ + struct thread_ctx *ctx = data; + + if (ctx->name != NULL) + of_thread_set_name(ctx->name); + + pthread_cleanup_push(free, data); + + ctx->function(ctx->object); + + pthread_cleanup_pop(1); + return NULL; +} + +bool +of_thread_attr_init(of_thread_attr_t *attr) +{ + pthread_attr_t pattr; + + if (pthread_attr_init(&pattr) != 0) + return false; + + @try { + attr->priority = 0; + + if (pthread_attr_getstacksize(&pattr, &attr->stackSize) != 0) + return false; + } @finally { + pthread_attr_destroy(&pattr); + } + + return true; +} + +bool +of_thread_new(of_thread_t *thread, const char *name, void (*function)(id), + id object, const of_thread_attr_t *attr) +{ + bool ret; + pthread_attr_t pattr; + + if (pthread_attr_init(&pattr) != 0) + return false; + + @try { + struct thread_ctx *ctx; + + if (attr != NULL) { + struct sched_param param; + + if (attr->priority < -1 || attr->priority > 1) { + errno = EINVAL; + return false; + } + +#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED + if (pthread_attr_setinheritsched(&pattr, + PTHREAD_EXPLICIT_SCHED) != 0) + return false; +#endif + + if (attr->priority < 0) { + param.sched_priority = minPrio + + (1.0f + attr->priority) * + (normalPrio - minPrio); + } else + param.sched_priority = normalPrio + + attr->priority * (maxPrio - normalPrio); + + if (pthread_attr_setschedparam(&pattr, ¶m) != 0) + return false; + + if (attr->stackSize > 0) { + if (pthread_attr_setstacksize(&pattr, + attr->stackSize) != 0) + return false; + } + } + + if ((ctx = malloc(sizeof(*ctx))) == NULL) { + errno = ENOMEM; + return false; + } + + ctx->function = function; + ctx->object = object; + ctx->name = name; + + ret = (pthread_create(thread, &pattr, + functionWrapper, ctx) == 0); + } @finally { + pthread_attr_destroy(&pattr); + } + + return ret; +} + +bool +of_thread_join(of_thread_t thread) +{ + void *ret; + + return (pthread_join(thread, &ret) == 0); +} + +bool +of_thread_detach(of_thread_t thread) +{ + return (pthread_detach(thread) == 0); +} + +void +of_thread_set_name(const char *name) +{ +#if defined(OF_HAIKU) + rename_thread(find_thread(NULL), name); +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + pthread_set_name_np(pthread_self(), name); +#elif defined(HAVE_PTHREAD_SETNAME_NP) +# if defined(OF_MACOS) || defined(OF_IOS) + pthread_setname_np(name); +# elif defined(__GLIBC__) + char buffer[16]; + + strncpy(buffer, name, 15); + buffer[15] = 0; + + pthread_setname_np(pthread_self(), buffer); +# endif +#endif +} ADDED src/platform/posix/tlskey.m Index: src/platform/posix/tlskey.m ================================================================== --- src/platform/posix/tlskey.m +++ src/platform/posix/tlskey.m @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "tlskey.h" + +bool +of_tlskey_new(of_tlskey_t *key) +{ + return (pthread_key_create(key, NULL) == 0); +} + +bool +of_tlskey_free(of_tlskey_t key) +{ + return (pthread_key_delete(key) == 0); +} ADDED src/platform/windows/OFString+PathAdditions.m Index: src/platform/windows/OFString+PathAdditions.m ================================================================== --- src/platform/windows/OFString+PathAdditions.m +++ src/platform/windows/OFString+PathAdditions.m @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +/* + * This file is also used for MS-DOS! Don't forget to #ifdef Windows-specific + * parts! + */ + +#include "config.h" + +#import "OFString+PathAdditions.h" +#import "OFArray.h" + +#import "OFOutOfRangeException.h" + +int _OFString_PathAdditions_reference; + +@implementation OFString (PathAdditions) ++ (OFString *)pathWithComponents: (OFArray *)components +{ + OFMutableString *ret = [OFMutableString string]; + void *pool = objc_autoreleasePoolPush(); + bool first = true; + + for (OFString *component in components) { + if (component.length == 0) + continue; + + if (!first && ![ret hasSuffix: @":"] && + ([component isEqual: @"\\"] || [component isEqual: @"/"])) + continue; + + if (!first && ![ret hasSuffix: @"\\"] && + ![ret hasSuffix: @"/"] && ![ret hasSuffix: @":"]) + [ret appendString: @"\\"]; + + [ret appendString: component]; + + first = false; + } + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (bool)isAbsolutePath +{ +#ifdef OF_WINDOWS + if ([self hasPrefix: @"\\\\"]) + return true; +#endif + + return ([self containsString: @":\\"] || [self containsString: @":/"]); +} + +- (OFArray *)pathComponents +{ + OFMutableArray OF_GENERIC(OFString *) *ret = [OFMutableArray array]; + void *pool = objc_autoreleasePoolPush(); + const char *cString = self.UTF8String; + size_t i, last = 0, cStringLength = self.UTF8StringLength; + bool isUNC = false; + + if (cStringLength == 0) { + objc_autoreleasePoolPop(pool); + return ret; + } + +#ifdef OF_WINDOWS + if ([self hasPrefix: @"\\\\"]) { + isUNC = true; + [ret addObject: @"\\\\"]; + + cString += 2; + cStringLength -= 2; + } +#endif + + for (i = 0; i < cStringLength; i++) { + if (cString[i] == '\\' || cString[i] == '/') { + if (i == 0) + [ret addObject: [OFString + stringWithUTF8String: cString + length: 1]]; + else if (i - last != 0) + [ret addObject: [OFString + stringWithUTF8String: cString + last + length: i - last]]; + + last = i + 1; + } else if (!isUNC && cString[i] == ':') { + if (i + 1 < cStringLength && + (cString[i + 1] == '\\' || cString[i + 1] == '/')) + i++; + + [ret addObject: [OFString + stringWithUTF8String: cString + last + length: i - last + 1]]; + + last = i + 1; + } + } + if (i - last != 0) + [ret addObject: [OFString stringWithUTF8String: cString + last + length: i - last]]; + + [ret makeImmutable]; + + objc_autoreleasePoolPop(pool); + + return ret; +} + +- (OFString *)lastPathComponent +{ + /* + * Windows/DOS need the full parsing to determine the last path + * component. This could be optimized by not creating the temporary + * objects, though. + */ + void *pool = objc_autoreleasePoolPush(); + OFString *ret = self.pathComponents.lastObject; + + if (ret == nil) { + objc_autoreleasePoolPop(pool); + return @""; + } + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)pathExtension +{ + void *pool = objc_autoreleasePoolPush(); + OFString *ret, *fileName; + size_t pos; + + fileName = self.lastPathComponent; + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + ret = [fileName substringWithRange: + of_range(pos + 1, fileName.length - pos - 1)]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByDeletingLastPathComponent +{ + /* + * Windows/DOS need the full parsing to delete the last path component. + * This could be optimized, though. + */ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components = self.pathComponents; + size_t count = components.count; + OFString *ret; + + if (count == 0) { + objc_autoreleasePoolPop(pool); + return @""; + } + + if (count == 1) { + OFString *firstComponent = components.firstObject; + + if ([firstComponent hasSuffix: @":"] || + [firstComponent hasSuffix: @":\\"] || + [firstComponent hasSuffix: @":/"] || + [firstComponent hasPrefix: @"\\"]) { + ret = [firstComponent retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; + } + + objc_autoreleasePoolPop(pool); + return @"."; + } + + components = [components objectsInRange: + of_range(0, components.count - 1)]; + ret = [OFString pathWithComponents: components]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByDeletingPathExtension +{ + void *pool; + OFMutableArray OF_GENERIC(OFString *) *components; + OFString *ret, *fileName; + size_t pos; + + if (self.length == 0) + return [[self copy] autorelease]; + + pool = objc_autoreleasePoolPush(); + components = [[self.pathComponents mutableCopy] autorelease]; + fileName = components.lastObject; + + pos = [fileName rangeOfString: @"." + options: OF_STRING_SEARCH_BACKWARDS].location; + if (pos == OF_NOT_FOUND || pos == 0) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + fileName = [fileName substringWithRange: of_range(0, pos)]; + [components replaceObjectAtIndex: components.count - 1 + withObject: fileName]; + + ret = [OFString pathWithComponents: components]; + + [ret retain]; + objc_autoreleasePoolPop(pool); + return [ret autorelease]; +} + +- (OFString *)stringByStandardizingPath +{ + void *pool = objc_autoreleasePoolPush(); + OFArray OF_GENERIC(OFString *) *components; + OFMutableArray OF_GENERIC(OFString *) *array; + OFString *ret; + bool done = false; + + if (self.length == 0) + return @""; + + components = self.pathComponents; + + if (components.count == 1) { + objc_autoreleasePoolPop(pool); + return [[self copy] autorelease]; + } + + array = [[components mutableCopy] autorelease]; + + while (!done) { + size_t length = array.count; + + done = true; + + for (size_t i = 0; i < length; i++) { + OFString *component = [array objectAtIndex: i]; + OFString *parent = + (i > 0 ? [array objectAtIndex: i - 1] : 0); + + if ([component isEqual: @"."] || + component.length == 0) { + [array removeObjectAtIndex: i]; + + done = false; + break; + } + + if ([component isEqual: @".."] && parent != nil && + ![parent isEqual: @".."] && + ![parent hasSuffix: @":"] && + ![parent hasSuffix: @":\\"] && + ![parent hasSuffix: @"://"] && + (![parent hasPrefix: @"\\"] || i != 1)) { + [array removeObjectsInRange: + of_range(i - 1, 2)]; + + done = false; + break; + } + } + } + + ret = [[OFString pathWithComponents: array] retain]; + + objc_autoreleasePoolPop(pool); + + return [ret autorelease]; +} + +- (OFString *)stringByAppendingPathComponent: (OFString *)component +{ + if ([self hasSuffix: @"\\"] || [self hasSuffix: @"/"]) + return [self stringByAppendingString: component]; + else { + OFMutableString *ret = [[self mutableCopy] autorelease]; + + [ret appendString: @"\\"]; + [ret appendString: component]; + + [ret makeImmutable]; + + return ret; + } +} +@end ADDED src/platform/windows/condition.m Index: src/platform/windows/condition.m ================================================================== --- src/platform/windows/condition.m +++ src/platform/windows/condition.m @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#import "condition.h" + +#include + +bool +of_condition_new(of_condition_t *condition) +{ + condition->count = 0; + + if ((condition->event = CreateEvent(NULL, FALSE, 0, NULL)) == NULL) { + errno = EAGAIN; + return false; + } + + return true; +} + +bool +of_condition_signal(of_condition_t *condition) +{ + if (!SetEvent(condition->event)) { + switch (GetLastError()) { + case ERROR_INVALID_HANDLE: + errno = EINVAL; + return false; + default: + OF_ENSURE(0); + } + } + + return true; +} + +bool +of_condition_broadcast(of_condition_t *condition) +{ + int count = condition->count; + + for (int i = 0; i < count; i++) { + if (!SetEvent(condition->event)) { + switch (GetLastError()) { + case ERROR_INVALID_HANDLE: + errno = EINVAL; + return false; + default: + OF_ENSURE(0); + } + } + } + + return true; +} + +bool +of_condition_wait(of_condition_t *condition, of_mutex_t *mutex) +{ + DWORD status; + + if (!of_mutex_unlock(mutex)) + return false; + + of_atomic_int_inc(&condition->count); + status = WaitForSingleObject(condition->event, INFINITE); + of_atomic_int_dec(&condition->count); + + switch (status) { + case WAIT_OBJECT_0: + return of_mutex_lock(mutex); + case WAIT_FAILED: + switch (GetLastError()) { + case ERROR_INVALID_HANDLE: + errno = EINVAL; + return false; + default: + OF_ENSURE(0); + } + default: + OF_ENSURE(0); + } +} + +bool +of_condition_timed_wait(of_condition_t *condition, of_mutex_t *mutex, + of_time_interval_t timeout) +{ + DWORD status; + + if (!of_mutex_unlock(mutex)) + return false; + + of_atomic_int_inc(&condition->count); + status = WaitForSingleObject(condition->event, timeout * 1000); + of_atomic_int_dec(&condition->count); + + switch (status) { + case WAIT_OBJECT_0: + return of_mutex_lock(mutex); + case WAIT_TIMEOUT: + errno = ETIMEDOUT; + return false; + case WAIT_FAILED: + switch (GetLastError()) { + case ERROR_INVALID_HANDLE: + errno = EINVAL; + return false; + default: + OF_ENSURE(0); + } + default: + OF_ENSURE(0); + } +} + +bool +of_condition_free(of_condition_t *condition) +{ + if (condition->count != 0) { + errno = EBUSY; + return false; + } + + return CloseHandle(condition->event); +} ADDED src/platform/windows/mutex.m Index: src/platform/windows/mutex.m ================================================================== --- src/platform/windows/mutex.m +++ src/platform/windows/mutex.m @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#import "mutex.h" + +#include + +bool +of_mutex_new(of_mutex_t *mutex) +{ + InitializeCriticalSection(mutex); + + return true; +} + +bool +of_mutex_lock(of_mutex_t *mutex) +{ + EnterCriticalSection(mutex); + + return true; +} + +bool +of_mutex_trylock(of_mutex_t *mutex) +{ + if (!TryEnterCriticalSection(mutex)) { + errno = EBUSY; + return false; + } + + return true; +} + +bool +of_mutex_unlock(of_mutex_t *mutex) +{ + LeaveCriticalSection(mutex); + + return true; +} + +bool +of_mutex_free(of_mutex_t *mutex) +{ + DeleteCriticalSection(mutex); + + return true; +} + +bool +of_rmutex_new(of_rmutex_t *rmutex) +{ + return of_mutex_new(rmutex); +} + +bool +of_rmutex_lock(of_rmutex_t *rmutex) +{ + return of_mutex_lock(rmutex); +} + +bool +of_rmutex_trylock(of_rmutex_t *rmutex) +{ + return of_mutex_trylock(rmutex); +} + +bool +of_rmutex_unlock(of_rmutex_t *rmutex) +{ + return of_mutex_unlock(rmutex); +} + +bool +of_rmutex_free(of_rmutex_t *rmutex) +{ + return of_mutex_free(rmutex); +} ADDED src/platform/windows/thread.m Index: src/platform/windows/thread.m ================================================================== --- src/platform/windows/thread.m +++ src/platform/windows/thread.m @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#include + +#import "thread.h" +#import "macros.h" + +#include + +bool +of_thread_attr_init(of_thread_attr_t *attr) +{ + attr->priority = 0; + attr->stackSize = 0; + + return true; +} + +bool +of_thread_new(of_thread_t *thread, const char *name, void (*function)(id), + id object, const of_thread_attr_t *attr) +{ + *thread = CreateThread(NULL, (attr != NULL ? attr->stackSize : 0), + (LPTHREAD_START_ROUTINE)function, (void *)object, 0, NULL); + + if (thread == NULL) { + switch (GetLastError()) { + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + return false; + case ERROR_ACCESS_DENIED: + errno = EACCES; + return false; + default: + OF_ENSURE(0); + } + } + + if (attr != NULL && attr->priority != 0) { + DWORD priority; + + if (attr->priority < -1 || attr->priority > 1) { + errno = EINVAL; + return false; + } + + if (attr->priority < 0) + priority = THREAD_PRIORITY_LOWEST + + (1.0 + attr->priority) * + (THREAD_PRIORITY_NORMAL - THREAD_PRIORITY_LOWEST); + else + priority = THREAD_PRIORITY_NORMAL + + attr->priority * + (THREAD_PRIORITY_HIGHEST - THREAD_PRIORITY_NORMAL); + + OF_ENSURE(!SetThreadPriority(*thread, priority)); + } + + return true; +} + +bool +of_thread_join(of_thread_t thread) +{ + switch (WaitForSingleObject(thread, INFINITE)) { + case WAIT_OBJECT_0: + CloseHandle(thread); + return true; + case WAIT_FAILED: + switch (GetLastError()) { + case ERROR_INVALID_HANDLE: + errno = EINVAL; + return false; + default: + OF_ENSURE(0); + } + default: + OF_ENSURE(0); + } +} + +bool +of_thread_detach(of_thread_t thread) +{ + CloseHandle(thread); + + return true; +} + +void +of_thread_set_name(const char *name) +{ +} ADDED src/platform/windows/tlskey.m Index: src/platform/windows/tlskey.m ================================================================== --- src/platform/windows/tlskey.m +++ src/platform/windows/tlskey.m @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "tlskey.h" + +bool +of_tlskey_new(of_tlskey_t *key) +{ + return ((*key = TlsAlloc()) != TLS_OUT_OF_INDEXES); +} + +bool +of_tlskey_free(of_tlskey_t key) +{ + return TlsFree(key); +} Index: src/thread.m ================================================================== --- src/thread.m +++ src/thread.m @@ -15,14 +15,14 @@ * file. */ #include "config.h" -#import "thread.h" +#include "platform.h" #if defined(OF_HAVE_PTHREADS) -# include "thread_pthread.m" +# include "platform/posix/thread.m" #elif defined(OF_WINDOWS) -# include "thread_winapi.m" +# include "platform/windows/thread.m" #elif defined(OF_AMIGAOS) -# include "thread_amiga.m" +# include "platform/amiga/thread.m" #endif DELETED src/thread_amiga.m Index: src/thread_amiga.m ================================================================== --- src/thread_amiga.m +++ src/thread_amiga.m @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include -#include - -#include -#include -#include - -#import "OFData.h" - -#import "tlskey.h" - -extern void of_tlskey_thread_exited(void); -static of_tlskey_t threadKey; - -OF_CONSTRUCTOR() -{ - OF_ENSURE(of_tlskey_new(&threadKey)); -} - -static void -functionWrapper(void) -{ - bool detached = false; - of_thread_t thread = - (of_thread_t)((struct Process *)FindTask(NULL))->pr_ExitData; - OF_ENSURE(of_tlskey_set(threadKey, thread)); - - thread->function(thread->object); - - ObtainSemaphore(&thread->semaphore); - @try { - thread->done = true; - - of_tlskey_thread_exited(); - - if (thread->detached) - detached = true; - else if (thread->joinTask != NULL) - Signal(thread->joinTask, (1ul << thread->joinSigBit)); - } @finally { - ReleaseSemaphore(&thread->semaphore); - } - - if (detached) - free(thread); -} - -bool -of_thread_attr_init(of_thread_attr_t *attr) -{ - attr->priority = 0; - attr->stackSize = 0; - - return true; -} - -bool -of_thread_new(of_thread_t *thread, const char *name, void (*function)(id), - id object, const of_thread_attr_t *attr) -{ - OFMutableData *tags = nil; - - if ((*thread = calloc(1, sizeof(**thread))) == NULL) { - errno = ENOMEM; - return false; - } - - @try { - (*thread)->function = function; - (*thread)->object = object; - InitSemaphore(&(*thread)->semaphore); - - tags = [[OFMutableData alloc] - initWithItemSize: sizeof(struct TagItem) - capacity: 12]; -#define ADD_TAG(tag, data) \ - { \ - struct TagItem t = { \ - .ti_Tag = tag, \ - .ti_Data = data \ - }; \ - [tags addItem: &t]; \ - } - ADD_TAG(NP_Entry, (ULONG)functionWrapper) - ADD_TAG(NP_ExitData, (ULONG)*thread) -#ifdef OF_AMIGAOS4 - ADD_TAG(NP_Child, TRUE) -#endif -#ifdef OF_MORPHOS - ADD_TAG(NP_CodeType, CODETYPE_PPC); -#endif - if (name != NULL) - ADD_TAG(NP_Name, (ULONG)name); - - ADD_TAG(NP_Input, ((struct Process *)FindTask(NULL))->pr_CIS) - ADD_TAG(NP_Output, ((struct Process *)FindTask(NULL))->pr_COS) - ADD_TAG(NP_Error, ((struct Process *)FindTask(NULL))->pr_CES) - ADD_TAG(NP_CloseInput, FALSE) - ADD_TAG(NP_CloseOutput, FALSE) - ADD_TAG(NP_CloseError, FALSE) - - if (attr != NULL && attr->priority != 0) { - if (attr->priority < 1 || attr->priority > 1) { - errno = EINVAL; - return false; - } - - /* - * -1 should be -128 (lowest possible priority) while - * +1 should be +127 (highest possible priority). - */ - ADD_TAG(NP_Priority, (attr->priority > 0 - ? attr->priority * 127 : attr->priority * 128)) - } - - if (attr != NULL && attr->stackSize != 0) - ADD_TAG(NP_StackSize, attr->stackSize) - else - ADD_TAG(NP_StackSize, - ((struct Process *)FindTask(NULL))->pr_StackSize) - - ADD_TAG(TAG_DONE, 0) -#undef ADD_TAG - - (*thread)->task = (struct Task *)CreateNewProc(tags.items); - if ((*thread)->task == NULL) { - free(*thread); - errno = EAGAIN; - return false; - } - } @catch (id e) { - free(*thread); - @throw e; - } @finally { - [tags release]; - } - - return true; -} - -of_thread_t -of_thread_current(void) -{ - return of_tlskey_get(threadKey); -} - -bool -of_thread_join(of_thread_t thread) -{ - ObtainSemaphore(&thread->semaphore); - @try { - if (thread->done) { - free(thread); - return true; - } - - if (thread->detached || thread->joinTask != NULL) { - errno = EINVAL; - return false; - } - - if ((thread->joinSigBit = AllocSignal(-1)) == -1) { - errno = EAGAIN; - return false; - } - - thread->joinTask = FindTask(NULL); - } @finally { - ReleaseSemaphore(&thread->semaphore); - } - - Wait(1ul << thread->joinSigBit); - FreeSignal(thread->joinSigBit); - - assert(thread->done); - free(thread); - - return true; -} - -bool -of_thread_detach(of_thread_t thread) -{ - ObtainSemaphore(&thread->semaphore); - - if (thread->done) - free(thread); - else - thread->detached = true; - - ReleaseSemaphore(&thread->semaphore); - - return true; -} - -void -of_thread_set_name(const char *name) -{ -} DELETED src/thread_pthread.m Index: src/thread_pthread.m ================================================================== --- src/thread_pthread.m +++ src/thread_pthread.m @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include - -#ifdef HAVE_PTHREAD_NP_H -# include -#endif - -#ifdef OF_HAIKU -# include -#endif - -#import "macros.h" - -static int minPrio = 0, maxPrio = 0, normalPrio = 0; - -struct thread_ctx { - void (*function)(id object); - id object; - const char *name; -}; - -/* - * This is done here to make sure this is done as early as possible in the main - * thread. - */ -OF_CONSTRUCTOR() -{ - pthread_attr_t pattr; - - if (pthread_attr_init(&pattr) == 0) { -#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY - int policy; -#endif - struct sched_param param; - -#ifdef HAVE_PTHREAD_ATTR_GETSCHEDPOLICY - if (pthread_attr_getschedpolicy(&pattr, &policy) == 0) { - minPrio = sched_get_priority_min(policy); - maxPrio = sched_get_priority_max(policy); - - if (minPrio == -1 || maxPrio == -1) - minPrio = maxPrio = 0; - } - - if (pthread_attr_getschedparam(&pattr, ¶m) != 0) - normalPrio = param.sched_priority; - else - minPrio = maxPrio = 0; - - pthread_attr_destroy(&pattr); -#endif - } -} - -static void * -functionWrapper(void *data) -{ - struct thread_ctx *ctx = data; - - if (ctx->name != NULL) - of_thread_set_name(ctx->name); - - pthread_cleanup_push(free, data); - - ctx->function(ctx->object); - - pthread_cleanup_pop(1); - return NULL; -} - -bool -of_thread_attr_init(of_thread_attr_t *attr) -{ - pthread_attr_t pattr; - - if (pthread_attr_init(&pattr) != 0) - return false; - - @try { - attr->priority = 0; - - if (pthread_attr_getstacksize(&pattr, &attr->stackSize) != 0) - return false; - } @finally { - pthread_attr_destroy(&pattr); - } - - return true; -} - -bool -of_thread_new(of_thread_t *thread, const char *name, void (*function)(id), - id object, const of_thread_attr_t *attr) -{ - bool ret; - pthread_attr_t pattr; - - if (pthread_attr_init(&pattr) != 0) - return false; - - @try { - struct thread_ctx *ctx; - - if (attr != NULL) { - struct sched_param param; - - if (attr->priority < -1 || attr->priority > 1) { - errno = EINVAL; - return false; - } - -#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED - if (pthread_attr_setinheritsched(&pattr, - PTHREAD_EXPLICIT_SCHED) != 0) - return false; -#endif - - if (attr->priority < 0) { - param.sched_priority = minPrio + - (1.0f + attr->priority) * - (normalPrio - minPrio); - } else - param.sched_priority = normalPrio + - attr->priority * (maxPrio - normalPrio); - - if (pthread_attr_setschedparam(&pattr, ¶m) != 0) - return false; - - if (attr->stackSize > 0) { - if (pthread_attr_setstacksize(&pattr, - attr->stackSize) != 0) - return false; - } - } - - if ((ctx = malloc(sizeof(*ctx))) == NULL) { - errno = ENOMEM; - return false; - } - - ctx->function = function; - ctx->object = object; - ctx->name = name; - - ret = (pthread_create(thread, &pattr, - functionWrapper, ctx) == 0); - } @finally { - pthread_attr_destroy(&pattr); - } - - return ret; -} - -bool -of_thread_join(of_thread_t thread) -{ - void *ret; - - return (pthread_join(thread, &ret) == 0); -} - -bool -of_thread_detach(of_thread_t thread) -{ - return (pthread_detach(thread) == 0); -} - -void -of_thread_set_name(const char *name) -{ -#if defined(OF_HAIKU) - rename_thread(find_thread(NULL), name); -#elif defined(HAVE_PTHREAD_SET_NAME_NP) - pthread_set_name_np(pthread_self(), name); -#elif defined(HAVE_PTHREAD_SETNAME_NP) -# if defined(OF_MACOS) || defined(OF_IOS) - pthread_setname_np(name); -# elif defined(__GLIBC__) - char buffer[16]; - - strncpy(buffer, name, 15); - buffer[15] = 0; - - pthread_setname_np(pthread_self(), buffer); -# endif -#endif -} DELETED src/thread_winapi.m Index: src/thread_winapi.m ================================================================== --- src/thread_winapi.m +++ src/thread_winapi.m @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * Jonathan Schleifer - * - * All rights reserved. - * - * This file is part of ObjFW. It may be distributed under the terms of the - * Q Public License 1.0, which can be found in the file LICENSE.QPL included in - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include - -#import "macros.h" - -bool -of_thread_attr_init(of_thread_attr_t *attr) -{ - attr->priority = 0; - attr->stackSize = 0; - - return true; -} - -bool -of_thread_new(of_thread_t *thread, const char *name, void (*function)(id), - id object, const of_thread_attr_t *attr) -{ - *thread = CreateThread(NULL, (attr != NULL ? attr->stackSize : 0), - (LPTHREAD_START_ROUTINE)function, (void *)object, 0, NULL); - - if (thread == NULL) { - switch (GetLastError()) { - case ERROR_NOT_ENOUGH_MEMORY: - errno = ENOMEM; - return false; - case ERROR_ACCESS_DENIED: - errno = EACCES; - return false; - default: - OF_ENSURE(0); - } - } - - if (attr != NULL && attr->priority != 0) { - DWORD priority; - - if (attr->priority < -1 || attr->priority > 1) { - errno = EINVAL; - return false; - } - - if (attr->priority < 0) - priority = THREAD_PRIORITY_LOWEST + - (1.0 + attr->priority) * - (THREAD_PRIORITY_NORMAL - THREAD_PRIORITY_LOWEST); - else - priority = THREAD_PRIORITY_NORMAL + - attr->priority * - (THREAD_PRIORITY_HIGHEST - THREAD_PRIORITY_NORMAL); - - OF_ENSURE(!SetThreadPriority(*thread, priority)); - } - - return true; -} - -bool -of_thread_join(of_thread_t thread) -{ - switch (WaitForSingleObject(thread, INFINITE)) { - case WAIT_OBJECT_0: - CloseHandle(thread); - return true; - case WAIT_FAILED: - switch (GetLastError()) { - case ERROR_INVALID_HANDLE: - errno = EINVAL; - return false; - default: - OF_ENSURE(0); - } - default: - OF_ENSURE(0); - } -} - -bool -of_thread_detach(of_thread_t thread) -{ - CloseHandle(thread); - - return true; -} - -void -of_thread_set_name(const char *name) -{ -} Index: src/tlskey.m ================================================================== --- src/tlskey.m +++ src/tlskey.m @@ -15,174 +15,14 @@ * file. */ #include "config.h" -#import "tlskey.h" - -#ifdef OF_AMIGAOS -# include -# include - -/* - * As we use this file in both the runtime and ObjFW, and since AmigaOS always - * has the runtime, use the hashtable from the runtime. - */ -# import "runtime/private.h" - -static of_tlskey_t firstKey = NULL, lastKey = NULL; -static struct SignalSemaphore semaphore; -static bool semaphoreInitialized = false; - -static uint32_t -hashFunc(const void *ptr) -{ - return (uint32_t)(uintptr_t)ptr; -} - -static bool -equalFunc(const void *ptr1, const void *ptr2) -{ - return (ptr1 == ptr2); -} - -OF_CONSTRUCTOR() -{ - if (!semaphoreInitialized) { - InitSemaphore(&semaphore); - semaphoreInitialized = true; - } -} -#endif - -bool -of_tlskey_new(of_tlskey_t *key) -{ -#if defined(OF_HAVE_PTHREADS) - return (pthread_key_create(key, NULL) == 0); -#elif defined(OF_WINDOWS) - return ((*key = TlsAlloc()) != TLS_OUT_OF_INDEXES); -#elif defined(OF_AMIGAOS) - if (!semaphoreInitialized) { - /* - * We might be called from another constructor, while ours has - * not run yet. This is safe, as the constructor is definitely - * run before a thread is spawned. - */ - InitSemaphore(&semaphore); - semaphoreInitialized = true; - } - - if ((*key = malloc(sizeof(**key))) == NULL) - return false; - - (*key)->table = NULL; - - ObtainSemaphore(&semaphore); - @try { - (*key)->next = NULL; - (*key)->previous = lastKey; - - if (lastKey != NULL) - lastKey->next = *key; - - lastKey = *key; - - if (firstKey == NULL) - firstKey = *key; - } @finally { - ReleaseSemaphore(&semaphore); - } - - /* We create the hash table lazily. */ - return true; -#endif -} - -bool -of_tlskey_free(of_tlskey_t key) -{ -#if defined(OF_HAVE_PTHREADS) - return (pthread_key_delete(key) == 0); -#elif defined(OF_WINDOWS) - return TlsFree(key); -#elif defined(OF_AMIGAOS) - ObtainSemaphore(&semaphore); - @try { - if (key->previous != NULL) - key->previous->next = key->next; - if (key->next != NULL) - key->next->previous = key->previous; - - if (firstKey == key) - firstKey = key->next; - if (lastKey == key) - lastKey = key->previous; - - objc_hashtable_free(key->table); - free(key); - } @finally { - ReleaseSemaphore(&semaphore); - } - - return true; -#endif -} - -#ifdef OF_AMIGAOS -void * -of_tlskey_get(of_tlskey_t key) -{ - void *ret; - - ObtainSemaphore(&semaphore); - @try { - if (key->table == NULL) - return NULL; - - ret = objc_hashtable_get(key->table, FindTask(NULL)); - } @finally { - ReleaseSemaphore(&semaphore); - } - - return ret; -} - -bool -of_tlskey_set(of_tlskey_t key, void *ptr) -{ - ObtainSemaphore(&semaphore); - @try { - struct Task *task = FindTask(NULL); - - if (key->table == NULL) - key->table = objc_hashtable_new(hashFunc, equalFunc, 2); - - if (ptr == NULL) - objc_hashtable_delete(key->table, task); - else - objc_hashtable_set(key->table, task, ptr); - } @catch (id e) { - return false; - } @finally { - ReleaseSemaphore(&semaphore); - } - - return true; -} - -void -of_tlskey_thread_exited(void) -{ - ObtainSemaphore(&semaphore); - @try { - struct Task *task = FindTask(NULL); - - for (of_tlskey_t iter = firstKey; iter != NULL; - iter = iter->next) - if (iter->table != NULL) - objc_hashtable_delete(iter->table, task); - } @finally { - ReleaseSemaphore(&semaphore); - } -} +#include "platform.h" + +#if defined(OF_HAVE_PTHREADS) +# include "platform/posix/tlskey.m" +#elif defined(OF_WINDOWS) +# include "platform/windows/tlskey.m" +#elif defined(OF_AMIGAOS) +# include "platform/amiga/tlskey.m" #endif