Index: src/OFDatagramSocket.h ================================================================== --- src/OFDatagramSocket.h +++ src/OFDatagramSocket.h @@ -103,10 +103,13 @@ */ @interface OFDatagramSocket: OFObject { OFSocketHandle _socket; +#ifdef OF_AMIGAOS + LONG _socketID; +#endif bool _canBlock; #ifdef OF_WII bool _canSendToBroadcastAddresses; #endif id _Nullable _delegate; @@ -295,10 +298,34 @@ receiver: (const OFSocketAddress *)receiver runLoopMode: (OFRunLoopMode)runLoopMode block: (OFDatagramSocketAsyncSendDataBlock)block; #endif +/** + * @brief Releases the socket from the current thread. + * + * This is necessary on some platforms in order to allow a different thread to + * use the socket, e.g. on AmigaOS, but you should call it on all operating + * systems before using the socket from a different thread. + * + * After calling this method, you must no longer use the socket until @ref + * obtainSocketForThread has been called. + */ +- (void)releaseSocketFromCurrentThread; + +/** + * @brief Obtains the socket for the current thread. + * + * This is necessary on some platforms in order to allow a different thread to + * use the socket, e.g. on AmigaOS, but you should call it on all operating + * systems before using the socket from a different thread. + * + * You must only call this method after @ref releaseSocketFromCurrentThread has + * been called from a different thread. + */ +- (void)obtainSocketForCurrentThread; + /** * @brief Cancels all pending asynchronous requests on the socket. */ - (void)cancelAsyncRequests; Index: src/OFDatagramSocket.m ================================================================== --- src/OFDatagramSocket.m +++ src/OFDatagramSocket.m @@ -31,13 +31,15 @@ #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFSocket.h" #import "OFSocket+Private.h" +#import "OFAlreadyOpenException.h" #import "OFGetOptionFailedException.h" #import "OFInitializationFailedException.h" #import "OFNotOpenException.h" +#import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFSetOptionFailedException.h" #import "OFSetOptionFailedException.h" #import "OFWriteFailedException.h" @@ -69,10 +71,13 @@ [self doesNotRecognizeSelector: _cmd]; abort(); } _socket = OFInvalidSocketHandle; +#ifdef OF_HAVE_AMIGAOS + _socketID = -1; +#endif _canBlock = true; } @catch (id e) { [self release]; @throw e; } @@ -404,10 +409,55 @@ @throw [OFOutOfRangeException exception]; return (int)_socket; #endif } + +- (void)releaseSocketFromCurrentThread +{ +#ifdef OF_AMIGAOS + if (_socket == OFInvalidSocketHandle) + @throw [OFNotOpenException exceptionWithObject: self]; + + if ((_socketID = ReleaseSocket(_socket, UNIQUE_ID)) == -1) { + switch (Errno()) { + case ENOMEM: + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: 0]; + case EBADF: + @throw [OFNotOpenException exceptionWithObject: self]; + default: + OFEnsure(0); + } + } + + _socket = OFInvalidSocketHandle; +#endif +} + +- (void)obtainSocketForCurrentThread +{ +#ifdef OF_AMIGAOS + if (_socket != OFInvalidSocketHandle) + @throw [OFAlreadyOpenException exceptionWithObject: self]; + + if (_socketID == -1) + @throw [OFNotOpenException exceptionWithObject: self]; + + /* + * FIXME: We should store these, but that requires changing all + * subclasses. This only becomes a problem if IPv6 support ever + * gets added. + */ + _socket = ObtainSocket(_socketID, AF_INET, SOCK_DGRAM, 0); + if (_socket == OFInvalidSocketHandle) + @throw [OFInitializationFailedException + exceptionWithClass: self.class]; + + _socketID = -1; +#endif +} - (void)close { if (_socket == OFInvalidSocketHandle) @throw [OFNotOpenException exceptionWithObject: self]; Index: src/OFSequencedPacketSocket.h ================================================================== --- src/OFSequencedPacketSocket.h +++ src/OFSequencedPacketSocket.h @@ -365,10 +365,34 @@ /** * @brief Cancels all pending asynchronous requests on the socket. */ - (void)cancelAsyncRequests; +/** + * @brief Releases the socket from the current thread. + * + * This is necessary on some platforms in order to allow a different thread to + * use the socket, e.g. on AmigaOS, but you should call it on all operating + * systems before using the socket from a different thread. + * + * After calling this method, you must no longer use the socket until @ref + * obtainSocketForThread has been called. + */ +- (void)releaseSocketFromCurrentThread; + +/** + * @brief Obtains the socket for the current thread. + * + * This is necessary on some platforms in order to allow a different thread to + * use the socket, e.g. on AmigaOS, but you should call it on all operating + * systems before using the socket from a different thread. + * + * You must only call this method after @ref releaseSocketFromCurrentThread has + * been called from a different thread. + */ +- (void)obtainSocketForCurrentThread; + /** * @brief Closes the socket so that it can neither receive nor send any more * datagrams. * * @throw OFNotOpenException The socket is not open Index: src/OFSequencedPacketSocket.m ================================================================== --- src/OFSequencedPacketSocket.m +++ src/OFSequencedPacketSocket.m @@ -464,10 +464,26 @@ @throw [OFOutOfRangeException exception]; return (int)_socket; #endif } + +- (void)releaseSocketFromCurrentThread +{ + /* + * Currently a nop, as all supported OSes that have SOCK_SEQPACKET do + * not need anything to move sockets between threads. + */ +} + +- (void)obtainSocketForCurrentThread +{ + /* + * Currently a nop, as all supported OSes that have SOCK_SEQPACKET do + * not need anything to move sockets between threads. + */ +} - (void)close { if (_socket == OFInvalidSocketHandle) @throw [OFNotOpenException exceptionWithObject: self]; Index: src/OFStreamSocket.h ================================================================== --- src/OFStreamSocket.h +++ src/OFStreamSocket.h @@ -64,10 +64,13 @@ */ @interface OFStreamSocket: OFStream { OFSocketHandle _socket; +#ifdef OF_AMIGAOS + LONG _socketID; +#endif bool _atEndOfStream, _listening; OFSocketAddress _remoteAddress; OF_RESERVE_IVARS(OFStreamSocket, 4) } @@ -159,8 +162,32 @@ * by the specified block as well. */ - (void)asyncAcceptWithRunLoopMode: (OFRunLoopMode)runLoopMode block: (OFStreamSocketAsyncAcceptBlock)block; #endif + +/** + * @brief Releases the socket from the current thread. + * + * This is necessary on some platforms in order to allow a different thread to + * use the socket, e.g. on AmigaOS, but you should call it on all operating + * systems before using the socket from a different thread. + * + * After calling this method, you must no longer use the socket until @ref + * obtainSocketForThread has been called. + */ +- (void)releaseSocketFromCurrentThread; + +/** + * @brief Obtains the socket for the current thread. + * + * This is necessary on some platforms in order to allow a different thread to + * use the socket, e.g. on AmigaOS, but you should call it on all operating + * systems before using the socket from a different thread. + * + * You must only call this method after @ref releaseSocketFromCurrentThread has + * been called from a different thread. + */ +- (void)obtainSocketForCurrentThread; @end OF_ASSUME_NONNULL_END Index: src/OFStreamSocket.m ================================================================== --- src/OFStreamSocket.m +++ src/OFStreamSocket.m @@ -29,15 +29,17 @@ #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFSocket+Private.h" #import "OFAcceptSocketFailedException.h" +#import "OFAlreadyOpenException.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFListenOnSocketFailedException.h" #import "OFNotImplementedException.h" #import "OFNotOpenException.h" +#import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFSetOptionFailedException.h" #import "OFWriteFailedException.h" @@ -69,10 +71,13 @@ [self doesNotRecognizeSelector: _cmd]; abort(); } _socket = OFInvalidSocketHandle; +#ifdef OF_AMIGAOS + _socketID = -1; +#endif } @catch (id e) { [self release]; @throw e; } @@ -357,10 +362,55 @@ if (_remoteAddress.length > (socklen_t)sizeof(_remoteAddress.sockaddr)) @throw [OFOutOfRangeException exception]; return &_remoteAddress; } + +- (void)releaseSocketFromCurrentThread +{ +#ifdef OF_AMIGAOS + if (_socket == OFInvalidSocketHandle) + @throw [OFNotOpenException exceptionWithObject: self]; + + if ((_socketID = ReleaseSocket(_socket, UNIQUE_ID)) == -1) { + switch (Errno()) { + case ENOMEM: + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: 0]; + case EBADF: + @throw [OFNotOpenException exceptionWithObject: self]; + default: + OFEnsure(0); + } + } + + _socket = OFInvalidSocketHandle; +#endif +} + +- (void)obtainSocketForCurrentThread +{ +#ifdef OF_AMIGAOS + if (_socket != OFInvalidSocketHandle) + @throw [OFAlreadyOpenException exceptionWithObject: self]; + + if (_socketID == -1) + @throw [OFNotOpenException exceptionWithObject: self]; + + /* + * FIXME: We should store these, but that requires changing all + * subclasses. This only becomes a problem if IPv6 support ever + * gets added. + */ + _socket = ObtainSocket(_socketID, AF_INET, SOCK_STREAM, 0); + if (_socket == OFInvalidSocketHandle) + @throw [OFInitializationFailedException + exceptionWithClass: self.class]; + + _socketID = -1; +#endif +} - (void)close { if (_socket == OFInvalidSocketHandle) @throw [OFNotOpenException exceptionWithObject: self];