0

I read a UIDocument incrementally and use its performAsynchronousFileAccessUsingBlock to facilitate this. Since I open the document on the main thread, I take care to also call this method on the main thread, but is that even necessary.

So my question is this - does it matter on which thread I call performAsynchronousFileAccessUsingBlock?

skaak
  • 2,988
  • 1
  • 8
  • 16

2 Answers2

0

Yes, it matters on what thread you call the perform function with block. Because other threads could change what file is meant to apply the block on ending up in unexpected/undefined behaviour. In other words you could use other threads to perform the block but you have to make sure there is something to apply on when the other threads are done and possibly the contents of it is released already.

you can read about iOS thread safe frameworks here https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html

And there is an explicit talk about accessing files with UIDocument from source that you changed for your needs on purpose. It's simple: it says it must be thread safe. https://developer.apple.com/documentation/uikit/uidocument#1658547

as

- (void)performAsynchronousFileAccessUsingBlock:(void (^)(void))block API_UNAVAILABLE(tvos);`

has a void return value and is running the block in its own background queue (thread). Queue means any call will end up in the same list of serialized blocks.

Coding with ^blocks lets you keep source code together so possible congruent work while the Queue is in process is easier to keep an eye on. In other words you do not need to create an extra thread nor do you need to call it from main thread. The docs only says opening, saving, closing is typically done from main thread.

The Framework offers a state for any UIDocument object. "A UIDocument object has a specific state at any moment in its life cycle", read more about it here.. https://developer.apple.com/documentation/uikit/uidocument#1658506

Hope that gives you a clue whats going on and how to.

Ol Sen
  • 3,163
  • 2
  • 21
  • 30
  • Thanks! I agree with your answer but if I understand it correctly it is from my perspective only. Thus you say be careful as your other threads may change the doc or the stuff in the block concurrently? This is all covered but I am asking, since I can call ```performAsynchronousFileAccessUsingBlock``` from any thread and the docs don't caution against it, will Apple / UIDocument sync properly at the back? Say I have two threads that concurrently call ```performAsynchronousFileAccessUsingBlock``` will it be fine or do I need to sync those? – skaak Jul 02 '20 at 10:54
  • A pointer to the allocated contents of the document may be reused by any other thread with any kind of possible behaviour unless you keep track of a needed looked/unlocked state while performing asynchronous tasks from different threads. Which is what the function does for you. In UIDocument comments it says: "use this method to serialize file access on a background queue" but there is explicit talk about how to when creating your own access design patterns https://developer.apple.com/documentation/uikit/uidocument/1619980-performasynchronousfileaccessusi?language=objc – Ol Sen Jul 03 '20 at 07:31
0

NO it does not matter.

Thanks Ol Sen - everything you say makes me think the answer is NO it does not matter but I am still not 100% convinced, so let me do a quick test with the code below. If it does not matter then this should run and if it does this should raise an exception somewhere. (What this does is to repeatedly call from both the main and a background thread).

    // Schedule on queue
    for ( int i = 0; i < 100; i ++ )
    {
        [self.ctl.queue addOperation:[NSBlockOperation blockOperationWithBlock: ^ {

            [self.doc performAsynchronousFileAccessUsingBlock: ^ {

                NSLog(@"Queue %d main %@",i,NSThread.isMainThread ? @"YES" : @"NO" );
                
            }];
            
        }]];
    }

    // Schedule on main
    for ( int i = 0; i < 10; i ++ )
    {
        [self.doc performAsynchronousFileAccessUsingBlock: ^ {
            
            NSLog(@"Main %d main %@",i,NSThread.isMainThread ? @"YES" : @"NO" );
            
        }];
    }

The result - it runs without any issue so now I have convinced myself and the answer is NO it does not matter. Clearly you can call this concurrently from any thread.

This is called on the main thread and self.doc is some arbitrary open UIDocument and self.ctl.queue is a background queue.

I know the docs say to typically use the main thread for those operations (open, save, close) but I once got into trouble for not abiding by that, so I have been careful to message UIDocument on the main thread only, but for this one clearly that is being too careful.

skaak
  • 2,988
  • 1
  • 8
  • 16
  • Exactly, this is what thread safe frameworks have to offer. Both performAsynchronousFileAccessUsingBlock calls place their blocks to same queue in background. And the creating thread does not have to be the main thread. – Ol Sen Jul 03 '20 at 19:16
  • Congruent workload for file access is more a question of design pattern to the work on the contents of the file which is possibly not in the assumed state the moment it's accessed from the queue. In short: you keep track of the serialisation of your commands by calling the perform-withBlock method in the sorting you want. – Ol Sen Jul 03 '20 at 19:24
  • Ol Sen!!!! Thanks again - you know, I think we agree on all points but your language is a bit too strong for me, so I struggle to know when to reply YES and when NO. – skaak Jul 03 '20 at 19:58