Skip to content

Instantly share code, notes, and snippets.

@TheDreamsWind
Created April 16, 2023 07:01
Show Gist options
  • Save TheDreamsWind/49bc1d11817828d627d3968364b08d5e to your computer and use it in GitHub Desktop.
Save TheDreamsWind/49bc1d11817828d627d3968364b08d5e to your computer and use it in GitHub Desktop.
[SO-a/75344837/5690248] An operation class with an arbitrary thread stack size
//
// TDWOperation.h
//
// Created by Aleksandr Medvedev on 16.04.2023.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void(^TDWOperationBlock)(void);
__attribute__((__objc_direct_members__))
@interface TDWOperation: NSOperation
@property (assign, nonatomic, readonly) size_t stackSize; // bytes
@property (copy, nonatomic) TDWOperationBlock executionBlock;
- (instancetype)initWithExecutionBlock:(TDWOperationBlock)block
stackSize:(size_t)stackSize /* bytes */ NS_DESIGNATED_INITIALIZER;
@end
NS_ASSUME_NONNULL_END
//
// TDWOperation.m
//
// Created by Aleksandr Medvedev on 16.04.2023.
//
#import "TDWOperation.h"
#import <pthread.h>
#define EXECUTE_WITH_ERROR(codeVar, execution) if((codeVar = execution)) {\
NSLog(@"Failed to execute " #execution " with error code: %d", codeVar);\
return;\
}
NS_ASSUME_NONNULL_BEGIN
__attribute__((__objc_direct_members__))
@interface TDWOperation ()
@property (assign, getter=tdw_p_isThreadStarted, setter=tdw_p_setThreadStarted:) BOOL tdw_p_threadStarted;
@property (assign, nonatomic) pthread_t tdw_p_underlyingThread;
@property (strong, nonatomic, readonly) dispatch_queue_t tdw_p_threadStartedSyncQueue;
@end
NS_ASSUME_NONNULL_END
@implementation TDWOperation
@synthesize tdw_p_threadStarted = _tdw_p_threadStarted;
#pragma mark Lifecycle
- (instancetype)initWithExecutionBlock:(TDWOperationBlock)block
stackSize:(size_t)stackSize {
if (self = [super init]) {
_stackSize = stackSize;
_executionBlock = block;
_tdw_p_threadStarted = NO;
_tdw_p_threadStartedSyncQueue = dispatch_queue_create("the.dreams.wind.property_access.TDWOperation.isThreadStarted",
DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (instancetype)init {
return [self initWithExecutionBlock:^{} stackSize:1024 * 1024 * 512];
}
#pragma mark NSOperation
- (void)main {
pthread_attr_t attrs;
int statusCode;
EXECUTE_WITH_ERROR(statusCode, pthread_attr_init(&attrs))
// Allocates 32 MiB stack size
EXECUTE_WITH_ERROR(statusCode, pthread_attr_setstacksize(&attrs, _stackSize))
pthread_t thread;
EXECUTE_WITH_ERROR(statusCode, pthread_create(&thread, &attrs, &tdw_p_runExecutionBlock, (__bridge_retained void *)self))
EXECUTE_WITH_ERROR(statusCode, pthread_attr_destroy(&attrs))
void* result = NULL;
if (!self.cancelled) {
self.tdw_p_threadStarted = YES;
EXECUTE_WITH_ERROR(statusCode, pthread_join(thread, &result));
self.tdw_p_threadStarted = NO;
}
NSLog(@"Main is finished with result: %d", *(int *)result);
free((int *)result);
}
#pragma mark Properties
- (void)setExecutionBlock:(TDWOperationBlock)executionBlock {
if (self.tdw_p_isThreadStarted) {
[NSException raise:NSInternalInconsistencyException
format:@"Cannot change execution block when execution is already started"];
}
_executionBlock = executionBlock;
}
- (BOOL)tdw_p_isThreadStarted {
__block BOOL result;
dispatch_sync(_tdw_p_threadStartedSyncQueue, ^{
result = _tdw_p_threadStarted;
});
return result;
}
- (void)tdw_p_setThreadStarted:(BOOL)threadStarted {
dispatch_barrier_async(_tdw_p_threadStartedSyncQueue, ^{
self->_tdw_p_threadStarted = threadStarted;
});
}
#pragma mark Private
void *tdw_p_runExecutionBlock(void *args) {
TDWOperation *self = (__bridge_transfer TDWOperation *)args;
if (self.executionBlock) {
self.executionBlock();
}
// Status code
int *result = calloc(1, sizeof(int));
return result;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment