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