/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3.0 only,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3.0 along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
#import "OFObject.h"
#ifdef OF_HAVE_SOCKETS
# import "OFSocket.h"
#endif
#ifdef OF_AMIGAOS
# include <exec/types.h>
# include <exec/tasks.h>
#endif
OF_ASSUME_NONNULL_BEGIN
@class OFMutableArray OF_GENERIC(ObjectType);
@class OFDate;
#ifdef OF_HAVE_THREADS
@class OFMutex;
#endif
@class OFMutableData;
/**
* @protocol OFKernelEventObserverDelegate
* OFKernelEventObserver.h ObjFW/OFKernelEventObserver.h
*
* @brief A protocol that needs to be implemented by delegates for
* OFKernelEventObserver.
*/
@protocol OFKernelEventObserverDelegate <OFObject>
@optional
/**
* @brief This callback is called when an object did get ready for reading.
*
* @note If the object is a subclass of @ref OFStream and
* @ref OFStream#tryReadLine or @ref OFStream#tryReadUntilDelimiter:
* has been called on the stream, this callback will not be called again
* until new data has been received, even though there is still data in
* the cache. The reason for this is to prevent spinning in a loop when
* there is an incomplete string in the cache. Once the string has been
* completed, the callback will be called again as long there is data in
* the cache.
*
* @param object The object which did become ready for reading
*/
- (void)objectIsReadyForReading: (id)object;
/**
* @brief This callback is called when an object did get ready for writing.
*
* @param object The object which did become ready for writing
*/
- (void)objectIsReadyForWriting: (id)object;
#if defined(OF_AMIGAOS) || defined(DOXYGEN)
/**
* @brief This callback is called when an Exec Signal was received.
*
* @note This is only available on AmigaOS!
*/
- (void)execSignalWasReceived: (ULONG)signalMask;
#endif
@end
/**
* @protocol OFReadyForReadingObserving
* OFKernelEventObserver.h ObjFW/OFKernelEventObserver.h
*
* @brief This protocol is implemented by classes which can be observed for
* readiness for reading by OFKernelEventObserver.
*/
@protocol OFReadyForReadingObserving <OFObject>
/**
* @brief The file descriptor for reading that should be checked by the
* OFKernelEventObserver.
*/
@property (readonly, nonatomic) int fileDescriptorForReading;
@end
/**
* @protocol OFReadyForWritingObserving
* OFKernelEventObserver.h ObjFW/OFKernelEventObserver.h
*
* @brief This protocol is implemented by classes which can be observed for
* readiness for writing by OFKernelEventObserver.
*/
@protocol OFReadyForWritingObserving <OFObject>
/**
* @brief The file descriptor for writing that should be checked by the
* OFKernelEventObserver.
*/
@property (readonly, nonatomic) int fileDescriptorForWriting;
@end
#ifdef OF_HAVE_SOCKETS
/**
* @class OFKernelEventObserver
* OFKernelEventObserver.h ObjFW/OFKernelEventObserver.h
*
* @brief A class that can observe multiple kernel events (e.g. streams being
* ready to read) at once.
*
* @note Currently, Win32 can only observe TCP and UDP sockets!
*/
@interface OFKernelEventObserver: OFObject
{
OFMutableArray OF_GENERIC(id <OFReadyForReadingObserving>)
*_readObjects;
OFMutableArray OF_GENERIC(id <OFReadyForWritingObserving>)
*_writeObjects;
id <OFKernelEventObserverDelegate> _Nullable _delegate;
# if defined(OF_AMIGAOS)
struct Task *_waitingTask;
ULONG _cancelSignal;
# elif defined(OF_HAVE_PIPE)
int _cancelFD[2];
# else
OFSocketHandle _cancelFD[2];
struct sockaddr_in _cancelAddr;
# endif
# ifdef OF_AMIGAOS
ULONG _execSignalMask;
# endif
OF_RESERVE_IVARS(OFKernelEventObserver, 4)
}
/**
* @brief The delegate for the OFKernelEventObserver.
*/
@property OF_NULLABLE_PROPERTY (assign, nonatomic)
id <OFKernelEventObserverDelegate> delegate;
# if defined(OF_AMIGAOS) || defined(DOXYGEN)
/**
* @brief A mask of Exec Signals to wait for.
*
* @note This is only available on AmigaOS!
*/
@property (nonatomic) ULONG execSignalMask;
# endif
/**
* @brief Creates a new OFKernelEventObserver.
*
* @return A new, autoreleased OFKernelEventObserver
*/
+ (instancetype)observer;
/**
* @brief Adds an object to observe for reading.
*
* This is also used to observe a listening socket for incoming connections,
* which then triggers a read event for the observed object.
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent blocking even though the newly added object is ready.
*
* @param object The object to observe for reading
* @throw OFObserveKernelEventsFailedException Adding the object for observing
* failed
*/
- (void)addObjectForReading: (id <OFReadyForReadingObserving>)object;
/**
* @brief Adds an object to observe for writing.
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent blocking even though the newly added object is ready.
*
* @param object The object to observe for writing
* @throw OFObserveKernelEventsFailedException Adding the object for observing
* failed
*/
- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object;
/**
* @brief Removes an object to observe for reading.
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent the removed object from still being observed.
*
* @param object The object to remove from observing for reading
* @throw OFObserveKernelEventsFailedException Removing the object for observing
* failed
*/
- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object;
/**
* @brief Removes an object to observe for writing.
*
* If there is an @ref observe call blocking, it will be canceled. The reason
* for this is to prevent the removed object from still being observed.
*
* @param object The object to remove from observing for writing
* @throw OFObserveKernelEventsFailedException Removing the object for observing
* failed
*/
- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object;
/**
* @brief Observes all objects and blocks until an event happens on an object.
*
* @throw OFObserveKernelEventsFailedException Observing for kernel events
* failed
*/
- (void)observe;
/**
* @brief Observes all objects until an event happens on an object or the
* timeout is reached.
*
* @param timeInterval The time to wait for an event, in seconds
* @throw OFObserveKernelEventsFailedException Observing for kernel events
* failed
*/
- (void)observeForTimeInterval: (OFTimeInterval)timeInterval;
/**
* @brief Observes all objects until an event happens on an object or the
* specified date is reached.
*
* @param date The until which to observe
* @throw OFObserveKernelEventsFailedException Observing for kernel events
* failed
*/
- (void)observeUntilDate: (OFDate *)date;
/**
* @brief Cancels the currently blocking observe call.
*
* This is the only method that can and should be called from another thread
* than the one using the observer.
*/
- (void)cancel;
/**
* @brief This method should be called by subclasses in @ref observeUntilDate:
* as the first thing to handle all sockets that currently have data in
* the read buffer.
*/
- (bool)of_processReadBuffers;
@end
#endif
OF_ASSUME_NONNULL_END