How to wait until an object is removed from Array

I have declared an NSMutableArray and the count should not exceed 100. If some one calls addObject method to add an item to that array when the count is 100 then that method call should not be executed until someone removes an item so that count will go down below 100. Can we use semaphore or group dispatch for signaling or mutex/NSLock is recommended.

Replies

So what does “should not be executed” mean in this context? Do you want the calling thread to block waiting for access? Or do you want the calling thread to return and then implement some sort of continuation mechanism?

I generally recommend the latter because the former can lead to thread explosion.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I have declared a class with NSMutableArray initialized and two methods to add and remove. The array count should not exceed 100.

If some one calls the method to add an item to that array when the count is 100 then that method should wait until the count goes down to 99 . I want to know how to make the addItem operation to wait until the array count goes down from 100 to 99.

then that method should wait until the count goes down to 99

Again, I need to know what you mean by “wait”. Do you want the thread to block?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Yes, Array count should not exceed 100. When the count reaches 100, add/insert operation should be blocked until delete operation happens so that buffer overflow will not happen.

You typically use a semaphore for this sort of thing:

  1. Initialise the semaphore with your limit.

  2. Before adding an item, do a wait on the semaphore.

  3. After releasing an item, do a send on the semaphore.

Our platforms support numerous semaphore APIs but the nicest one, at least IMO, is the one in Dispatch. To get started, see the dispatch_semaphore_create man page.


Having said that, I’m concerned about the low-level concurrency primitives you’re reaching for in this and your other thread. The only reason to initialise your semaphore to 100 is to let up to 100 threads work on some operation simultaneously. That’s not a good design on our platforms, where hardware core counts are usually an order of magitude less. The end result is that you’ll be wasting resources tracking all of those threads and having the kernel switch between them.

Can you explain more about your high-level goal here?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

here is the use case.

App will send multiple NSURLSession request to download the catalog and will be adding the catalog downloaded from the server to array. App should not display more that 100 catalog/products. If downloaded product count exceeds zero, app should stop adding product to array and app should block add operation. When app deletes the item(s) then add operation should be unblocked as count will be less than 100.

Quinn- Can you give a code snippet on how to implement? Thanks!

App will send multiple NSURLSession request to download the catalog and will be adding the catalog downloaded from the server to array.

If you’re using NSURLSession then you definitely should not block your thread. That design means there could be up to 100 threads running NSURLSession requests, and that needlessly squanders resources because the network is fundamentally single thread.

A better option is to use continuations. That is, rather than use multiple threads, encapsulate the work to be done into a block. Then you can keep a count of the number of outstanding blocks and, if a new one comes in while too many are outstanding, queue that block until one of the previous ones finish.

Something like this:

typedef void (^CompletionHandler)(void);
typedef void (^Work)(CompletionHandler completionHandler);

@property (nonatomic, strong, readonly) dispatch_queue_t queue;
@property (nonatomic, assign, readwrite) NSInteger outstanding;
@property (nonatomic, strong, readonly) NSMutableArray * queuedWork;

- (void)runWork:(Work)work {
    dispatch_async(self.queue, ^{
        [self.queuedWork addObject:work];
        [self startWorkIfPosible];
    });
}

- (void)startWorkIfPosible {
    dispatch_assert_queue(self.queue);
    while ( (self.outstanding < 100) && (self.queuedWork.count != 0) ) {
        Work work = self.queuedWork.firstObject;
        [self.queuedWork removeObjectAtIndex:0];
        self.outstanding += 1;
        work(^{
            dispatch_async(self.queue, ^{
                self.outstanding -= 1;
                [self startWorkIfPosible];
            });
        });
    }
}

Finally, while some overlap makes sense with NSURLSession, and how much depends on whether your server supports HTTP/2, running 100 requests simultaneously is kinda pointless.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"