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