pEpObjCAdapter/PEPSessionProvider.m
author Dirk Zimmermann <dirk@pep-project.org>
Tue, 20 Mar 2018 13:03:07 +0100
changeset 549 a338e3afd9c2
parent 548 09ec46b2113d
child 550 c6ac36a539f3
permissions -rw-r--r--
IOS-975 avoid race condition in tests when ensuring the main session
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@416
    11
#import "PEPObjCAdapter+Internal.h"
andreas@269
    12
#import "PEPInternalSession.h"
andreas@262
    13
#import "PEPCopyableThread.h"
andreas@262
    14
andreas@262
    15
@implementation PEPSessionProvider
andreas@262
    16
andreas@262
    17
static NSLock *s_sessionForThreadLock = nil;
andreas@269
    18
static NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *s_sessionForThreadDict;
andreas@262
    19
andreas@290
    20
/** We have to conform to the Engines rule: "The first session has to be created on the main thread and kept
andreas@290
    21
 alive until all sessiopns created afterwards have been teared down."
andreas@290
    22
 Here we hold it.
andreas@290
    23
 */
andreas@290
    24
static PEPInternalSession *s_sessionForMainThread = nil;
andreas@290
    25
andreas@262
    26
#pragma mark - Public API
andreas@262
    27
andreas@269
    28
+ (PEPInternalSession * _Nonnull)session
andreas@262
    29
{
andreas@262
    30
    [[self sessionForThreadLock] lock];
andreas@273
    31
andreas@290
    32
    // Assure a session for the main thread exists and is kept alive before anyother session is created.
andreas@290
    33
    [self assureSessionForMainThreadExists];
andreas@290
    34
andreas@290
    35
    if ([NSThread isMainThread]) {
andreas@292
    36
        [[self sessionForThreadLock] unlock];
andreas@290
    37
        return s_sessionForMainThread;
andreas@290
    38
    }
andreas@290
    39
andreas@288
    40
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@262
    41
    PEPCopyableThread *currentThread = [[PEPCopyableThread alloc] initWithThread:[NSThread currentThread]];
andreas@269
    42
    PEPInternalSession *session = dict[currentThread];
andreas@262
    43
    if (!session) {
andreas@297
    44
        session = [PEPInternalSession new];
andreas@262
    45
        dict[currentThread] = session;
andreas@262
    46
    }
andreas@416
    47
    [self setConfigUnencryptedSubjectOnSession:session];
andreas@272
    48
    [self nullifySessionsOfFinishedThreads];
andreas@273
    49
andreas@262
    50
    [[self sessionForThreadLock] unlock];
andreas@273
    51
andreas@262
    52
    return session;
andreas@262
    53
}
andreas@262
    54
andreas@262
    55
+ (void)cleanup
andreas@262
    56
{
andreas@262
    57
    [[self sessionForThreadLock] lock];
andreas@273
    58
andreas@273
    59
    [self cleanupInternal];
andreas@273
    60
andreas@262
    61
    [[self sessionForThreadLock] unlock];
andreas@262
    62
}
andreas@262
    63
andreas@262
    64
#pragma mark - Life Cycle
andreas@262
    65
andreas@262
    66
+ (void)initialize
andreas@262
    67
{
andreas@262
    68
    s_sessionForThreadLock = [NSLock new];
andreas@262
    69
    s_sessionForThreadDict = [NSMutableDictionary new];
andreas@262
    70
}
andreas@262
    71
andreas@262
    72
#pragma mark - Lock
andreas@262
    73
andreas@262
    74
+ (NSLock *)sessionForThreadLock
andreas@262
    75
{
andreas@262
    76
    return s_sessionForThreadLock;
andreas@262
    77
}
andreas@262
    78
andreas@262
    79
+ (NSMutableDictionary *)sessionForThreadDict
andreas@262
    80
{
andreas@262
    81
    return s_sessionForThreadDict;
andreas@262
    82
}
andreas@262
    83
andreas@262
    84
#pragma mark -
andreas@416
    85
andreas@416
    86
+ (void)setConfigUnencryptedSubjectOnSession:(PEPInternalSession *)session
andreas@416
    87
{
andreas@416
    88
    BOOL unencryptedSubjectEnabled = [PEPObjCAdapter unecryptedSubjectEnabled];
andreas@416
    89
    [session configUnencryptedSubjectEnabled:unencryptedSubjectEnabled];
andreas@416
    90
}
andreas@416
    91
andreas@290
    92
/**
andreas@290
    93
 Assures a session for the main thread is set.
andreas@290
    94
 */
andreas@290
    95
+ (void)assureSessionForMainThreadExists
andreas@290
    96
{
dirk@549
    97
    if (s_sessionForMainThread) {
dirk@549
    98
        return;
dirk@549
    99
    }
dirk@549
   100
dirk@548
   101
    void (^creationBlock)(void) = ^{
dirk@548
   102
        if (s_sessionForMainThread) {
dirk@548
   103
            return;
dirk@548
   104
        }
dirk@548
   105
        s_sessionForMainThread = [PEPInternalSession new];
dirk@548
   106
        [self setConfigUnencryptedSubjectOnSession:s_sessionForMainThread];
dirk@548
   107
    };
dirk@548
   108
andreas@290
   109
andreas@290
   110
    if ([NSThread isMainThread]) {
dirk@548
   111
        creationBlock();
andreas@290
   112
    } else {
dirk@548
   113
        dispatch_sync(dispatch_get_main_queue(), creationBlock);
andreas@290
   114
    }
andreas@290
   115
}
andreas@262
   116
andreas@273
   117
+ (void)cleanupInternal
andreas@273
   118
{
andreas@273
   119
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@273
   120
andreas@273
   121
    for (PEPCopyableThread *thread in dict.allKeys) {
andreas@275
   122
        [self nullifySessionForThread:thread];
andreas@273
   123
    }
andreas@290
   124
    s_sessionForMainThread = nil;
andreas@288
   125
    [dict removeAllObjects];
andreas@273
   126
}
andreas@273
   127
andreas@273
   128
/**
andreas@273
   129
 Tears down all sessions that belong to a thread that has finish executing forever.
andreas@273
   130
 */
andreas@272
   131
+ (void)nullifySessionsOfFinishedThreads
andreas@262
   132
{
andreas@269
   133
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@262
   134
    for (PEPCopyableThread *thread in dict.allKeys) {
andreas@262
   135
        if (thread.isFinished) {
andreas@273
   136
            // The session has been created to run on the one and only thread.
andreas@273
   137
            // As this thread did finish executing forever, we can be sure the session can not be
andreas@273
   138
            // accessed anymore, thus it is OK to nullify it on another thread as the one it has been
andreas@273
   139
            // created for.
andreas@262
   140
            [self nullifySessionForThread:thread];
andreas@262
   141
        }
andreas@262
   142
    }
andreas@262
   143
}
andreas@262
   144
andreas@262
   145
+ (void)nullifySessionForThread:(PEPCopyableThread *)thread
andreas@262
   146
{
andreas@269
   147
    NSMutableDictionary<PEPCopyableThread*,PEPInternalSession*> *dict = [self sessionForThreadDict];
andreas@262
   148
    dict[thread] = nil;
andreas@262
   149
}
andreas@262
   150
andreas@262
   151
@end