pEpObjCAdapter/PEPSessionProvider.m
author buff <andreas@pep-project.org>
Fri, 20 Oct 2017 12:47:55 +0200
branchIOSAD-50
changeset 290 19e6db3d1767
parent 288 a96780f56daa
child 292 ee62eebf395b
permissions -rw-r--r--
IOSAD-50 we take over the responsibility for creating and holding a session for the main thread
andreas@262
     1
//
andreas@262
     2
//  PEPSessionProvider.m
andreas@262
     3
//  pEpObjCAdapter
andreas@262
     4
//
andreas@262
     5
//  Created by Andreas Buff on 09.10.17.
andreas@262
     6
//  Copyright © 2017 p≡p. All rights reserved.
andreas@262
     7
//
andreas@262
     8
andreas@262
     9
#import "PEPSessionProvider.h"
andreas@262
    10
andreas@269
    11
#import "PEPInternalSession.h"
andreas@262
    12
#import "PEPCopyableThread.h"
andreas@262
    13
andreas@275
    14
/**
andreas@275
    15
 Restricts access to PEPInternalSession init to the one and only PEPSessionProvider. 
andreas@275
    16
 */
andreas@275
    17
@interface PEPInternalSession()
andreas@275
    18
- (instancetype)initInternal;
andreas@275
    19
@end
andreas@275
    20
andreas@262
    21
@implementation PEPSessionProvider
andreas@262
    22
andreas@262
    23
static NSLock *s_sessionForThreadLock = nil;
andreas@269
    24
static NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *s_sessionForThreadDict;
andreas@262
    25
andreas@290
    26
/** We have to conform to the Engines rule: "The first session has to be created on the main thread and kept
andreas@290
    27
 alive until all sessiopns created afterwards have been teared down."
andreas@290
    28
 Here we hold it.
andreas@290
    29
 */
andreas@290
    30
static PEPInternalSession *s_sessionForMainThread = nil;
andreas@290
    31
andreas@262
    32
#pragma mark - Public API
andreas@262
    33
andreas@269
    34
+ (PEPInternalSession * _Nonnull)session
andreas@262
    35
{
andreas@262
    36
    [[self sessionForThreadLock] lock];
andreas@273
    37
andreas@290
    38
    // Assure a session for the main thread exists and is kept alive before anyother session is created.
andreas@290
    39
    [self assureSessionForMainThreadExists];
andreas@290
    40
andreas@290
    41
    if ([NSThread isMainThread]) {
andreas@290
    42
        return s_sessionForMainThread;
andreas@290
    43
    }
andreas@290
    44
andreas@288
    45
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@262
    46
    PEPCopyableThread *currentThread = [[PEPCopyableThread alloc] initWithThread:[NSThread currentThread]];
andreas@269
    47
    PEPInternalSession *session = dict[currentThread];
andreas@262
    48
    if (!session) {
andreas@275
    49
        session = [[PEPInternalSession alloc] initInternal];
andreas@262
    50
        dict[currentThread] = session;
andreas@262
    51
    }
andreas@272
    52
    [self nullifySessionsOfFinishedThreads];
andreas@273
    53
andreas@288
    54
//    NSLog(@"#################\nnum sessions is now %lu",
andreas@290
    55
//          (unsigned long)[self sessionForThreadDict].count + (s_sessionForMainThread ? 1 : 0));
andreas@286
    56
//    NSLog(@"Threads:");
andreas@286
    57
//    for (PEPCopyableThread *thread in dict.allKeys) {
andreas@286
    58
//        NSLog(@"%@", thread.description);
andreas@286
    59
//    }
andreas@290
    60
//    NSLog(@"Session for main thread: %@", s_sessionForMainThread);
andreas@288
    61
//    NSLog(@"##################################");
andreas@288
    62
andreas@262
    63
    [[self sessionForThreadLock] unlock];
andreas@273
    64
andreas@262
    65
    return session;
andreas@262
    66
}
andreas@262
    67
andreas@262
    68
+ (void)cleanup
andreas@262
    69
{
andreas@262
    70
    [[self sessionForThreadLock] lock];
andreas@273
    71
andreas@273
    72
    [self cleanupInternal];
andreas@273
    73
andreas@262
    74
    [[self sessionForThreadLock] unlock];
andreas@262
    75
}
andreas@262
    76
andreas@262
    77
#pragma mark - Life Cycle
andreas@262
    78
andreas@262
    79
+ (void)initialize
andreas@262
    80
{
andreas@262
    81
    s_sessionForThreadLock = [NSLock new];
andreas@262
    82
    s_sessionForThreadDict = [NSMutableDictionary new];
andreas@262
    83
}
andreas@262
    84
andreas@262
    85
#pragma mark - Lock
andreas@262
    86
andreas@262
    87
+ (NSLock *)sessionForThreadLock
andreas@262
    88
{
andreas@262
    89
    return s_sessionForThreadLock;
andreas@262
    90
}
andreas@262
    91
andreas@262
    92
+ (NSMutableDictionary *)sessionForThreadDict
andreas@262
    93
{
andreas@262
    94
    return s_sessionForThreadDict;
andreas@262
    95
}
andreas@262
    96
andreas@262
    97
#pragma mark -
andreas@290
    98
/**
andreas@290
    99
 Assures a session for the main thread is set.
andreas@290
   100
 */
andreas@290
   101
+ (void)assureSessionForMainThreadExists
andreas@290
   102
{
andreas@290
   103
    if (s_sessionForMainThread) {
andreas@290
   104
        return;
andreas@290
   105
    }
andreas@290
   106
andreas@290
   107
    if ([NSThread isMainThread]) {
andreas@290
   108
        s_sessionForMainThread = [[PEPInternalSession alloc] initInternal];
andreas@290
   109
    } else {
andreas@290
   110
        dispatch_sync(dispatch_get_main_queue(), ^{
andreas@290
   111
            s_sessionForMainThread = [[PEPInternalSession alloc] initInternal];
andreas@290
   112
        });
andreas@290
   113
    }
andreas@290
   114
}
andreas@262
   115
andreas@273
   116
+ (void)cleanupInternal
andreas@273
   117
{
andreas@273
   118
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@273
   119
andreas@273
   120
    for (PEPCopyableThread *thread in dict.allKeys) {
andreas@275
   121
        [self nullifySessionForThread:thread];
andreas@273
   122
    }
andreas@290
   123
    s_sessionForMainThread = nil;
andreas@288
   124
    [dict removeAllObjects];
andreas@290
   125
//    NSLog(@"All sessions have been cleaned up. Session count is %lu",
andreas@290
   126
//          (unsigned long)dict.count + (s_sessionForMainThread ? 1 : 0));
andreas@273
   127
}
andreas@273
   128
andreas@273
   129
/**
andreas@273
   130
 Tears down all sessions that belong to a thread that has finish executing forever.
andreas@273
   131
 */
andreas@272
   132
+ (void)nullifySessionsOfFinishedThreads
andreas@262
   133
{
andreas@269
   134
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@262
   135
    for (PEPCopyableThread *thread in dict.allKeys) {
andreas@262
   136
        if (thread.isFinished) {
andreas@273
   137
            // The session has been created to run on the one and only thread.
andreas@273
   138
            // As this thread did finish executing forever, we can be sure the session can not be
andreas@273
   139
            // accessed anymore, thus it is OK to nullify it on another thread as the one it has been
andreas@273
   140
            // created for.
andreas@262
   141
            [self nullifySessionForThread:thread];
andreas@262
   142
        }
andreas@262
   143
    }
andreas@262
   144
}
andreas@262
   145
andreas@262
   146
+ (void)nullifySessionForThread:(PEPCopyableThread *)thread
andreas@262
   147
{
andreas@269
   148
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@269
   149
    PEPInternalSession *session = dict[thread];
andreas@262
   150
    dict[thread] = nil;
andreas@262
   151
    session = nil;
andreas@262
   152
}
andreas@262
   153
andreas@262
   154
@end