0

I have an iOS application using NSThreads for concurrency tasks. I will try to migrate it to be using the Grand Central Dispatch (GCD) for handling concurrency.

The problem is that the app needs information regarding how many threads has been created since a given time. And how many threads that was spawned since that given time is currently running.

At the moment this is done by creating a category that does a method swizzling on the -main method in NSThread. In the new swizzled method it simply increments the total number of threads running and then decrement the same variable before the new swizzled -main method returns.

The problem is that when I use GCD dispatch_async it does not create a NSThread, hence my category approach does not work. How can I achieve the same while using GCD to handle concurrency?

What I would like to detect is when a new block is added to GCD, and when that block has been executed.

Any suggestions on how to achieve the same is very welcome.

EDIT

Many thanks to @ipmcc and @RyanR for helping me out on this. :) I believe I need to tell some more about the background and what I am trying to accomplish.

What I am actually trying is to extend the iOS testing framework Frank. Frank embeds a small web-server within a given app which enables sending HTTP request to the iOS application and thereby simulating events, a swipe or a tap gesture as an example.

I would like to extend it in a way that enables it to wait until all work triggered by a specific simulated event has ended before returning upon a request.

However I found it hard to detect exactly what work was triggered by the received event. And thats how I came to the solution to just reset a thread counter and then increment this counter for all created threads after the event was simulated, and decrement it when the threads are finishing. And then block until threads count became zero again. I know this approach is not perfect either, and it wont work with GCP.

Is there any other way to achieve it? Another possible solution which I have thought of is to specify that everything must run synchronized except the thread handling the HTTP request. However I don't know if this possible.

Any suggestions on how to achieve blocking after each simulated event until work triggered by that event has completed?

Gabriele Petronella
  • 106,943
  • 21
  • 217
  • 235
dynamokaj
  • 471
  • 7
  • 19

2 Answers2

3

The problem is that the app needs information regarding how many threads has been created since a given time. And how many threads that was spawned since that given time is currently running.

You will not be able to get this information from GCD. One of the points of GCD is that you do not manage the thread pool. It is opaque. You'll note that even pthreads, the underlying threading library on which NSThread and GCD are built, does not have a (public) means to enumerate all existing threads or get the number of running threads. This is not going to be doable without hard core low level hackery. If you need to control or know the number of threads, then you need to be the one to spawn and manage them, and GCD is the wrong abstraction for you.

At the moment this is done by creating a category that does a method swizzling on the -main method in NSThread. In the new swizzled method it simply increments the total number of threads running and then decrement the same variable before the new swizzled -main method returns.

Note that this only tells you the number of threads started using NSThread. As mentioned, NSThread is a fairly high level abstraction on top of pthreads. There is nothing to prevent library code from spawning its own threads using the pthreads API that will be invisible to your count.

The problem is that when I use GCD dispatch_async it does not create a NSThread, hence my category approach does not work. How can I achieve the same while using GCD to handle concurrency?

In short, you can't. If you want to go forth and patch functions all over the various frameworks, then you should look up a library called mach_override. (But please don't.)

What I would like to detect is when a new block is added to GCD, and when that block has been executed.

Since GCD uses thread pools, the act of adding a block does not imply a new thread. (And that's sorta the whole point.)

If you have some limited resource whose consumption you need to manage, the traditional way to do that would be with a limiting semaphore, but that is just one option.

This whole question just reeks of a poor design. Like the number of pthreads, GCD's queue widths are opaque/non-public. Your previous solution was not particularly viable (as discussed), and further efforts are likely to yield similarly poor solutions. You should really rethink your architecture such that knowing how many threads are running isn't important.

EDIT: Thanks for the clarification. There's not really a generic way, from the outside, to tell when all the "work" is done. What if an action sets up a timer that won't call back for ten minutes? At the extreme, consider this: the main runloop continues to spin for the entire life of the app, and as long as the main runloop is spinning, "work" could be being done on it.

In order to detect "doneness" your app has to signal doneness. In order to signal doneness, the app has to have some way (internal to itself) to know it's done. Put differently, the app can't tell something else (i.e. Frank) something it doesn't know. One way to go about this would be to encapsulate all the work you do in your app in NSOperations. NSOperation/NSOperationQueue provide good ways of reporting "doneness." At the simplest level, you could wrap the code where you kickoff work in an NSBlockOperation, then add a completion block to that operation that signals something else when it's done, and enqueue it to an NSOperationQueue for execution. (You could also do this with dispatch_group and dispatch_group_notify if you prefer working in the GCD style.)

If you have specific questions about how to package up your app's work into NSOperations, I would suggest starting a new question.

ipmcc
  • 29,581
  • 5
  • 84
  • 147
  • Thanks alot for you answer. Based on your answer I can see that I cannot achieve what I want using the approach I had in mind. I have updated my question to describe more clearly what I am trying to accomplish. If you have the time, please have a look. Thank you very much! :) – dynamokaj Oct 19 '13 at 01:23
2

You can hook into the dispatch introspection functions (introspection.h, methods all start with dispatch_introspection), but you have to link with that library which is supposed to be only for debugging. I don't think you can include that in a release build. Your best bet would be to encapsulate GCD into your own object, so all your code submits blocks to execute through that object and it submits them to GCD after tracking whatever you're interested in. You won't be able to track thread consumption though, because GCD intentionally abstracts that and reuses threads.

RyanR
  • 7,728
  • 1
  • 25
  • 39
  • Do you think this introspection can tell me if all blocks on a given queue has completed execution, and not only if the queue is empty or not? BTW I have updated my original question to describe more detailed what I am trying to accomplish. – dynamokaj Oct 19 '13 at 01:26
  • Introspection can notify you when a block is enqueued or dequeued, but not when the queue is empty – RyanR Oct 19 '13 at 13:00
  • Okay, but can it tell me when a block dequeued has completed? – dynamokaj Oct 19 '13 at 13:56
  • No, and nothing can. That block could enqueue other blocks on other queues, schedule timers, any number of async tasks that make it impossible to tell when the block is 'done'. You need to rethink how you are trying to design this, because it isn't going to work. – RyanR Oct 19 '13 at 14:01
  • My idea was to increment a counter each time a block is enqueued and decrement the same counter when it is dequeued and completed. If that given block enqueues other blocks while running, they would increment and decrement the counter as well. Have you any other ideas on how I else can detect when an event has ended an all the tasks it triggered has ended.? – dynamokaj Oct 19 '13 at 14:44
  • What if that block queues an NSOperation, starts an NSThread, or even a pthread? If you only care about blocks, yes a counter will work. But if you really want to know when all code as a result of a block is done executing, I don't think it's possible. Your best bet would be to require all blocks get enqueued through some object you own, and the blocks must call a designated completion handler when they're done. – RyanR Oct 19 '13 at 17:04
  • What I would really like to know is when all code as a result of an input event (tap, swipe etc.) has been executed before returning. However I do not think this is possible either, unless I can force everything to run on the main thread only. This is only for testing purpose, as part of a test framework. Do you think it is possible to force all code to be executed in the main thread, maybe even syncronized in the main thread? – dynamokaj Oct 19 '13 at 17:58
  • I believe a possible solution could be to extend /usr/lib/system/libdispatch.dylib since it is open source and then each time a block is added. I will wrap it in another block whichs only purpose is to increment and decrement a counter when it starts and when it finished. Then I could retrieve this counter. I will try to see if this is possible. – dynamokaj Oct 19 '13 at 19:58
  • I don't think you can force all code to run on a single thread. As for extending libdispatch, last I checked Apple had released an open source implementation, but it was not the implementation OSX/iOS used (I could be wrong on this) – RyanR Oct 19 '13 at 20:42
  • Is it even possible to change the libdispatch.dylib for iOS? Is it possible for me to replace the libdispatch.dylib on iOS with my customized one?. I know it is possible on OSX, but I don't know about iOS. – dynamokaj Oct 20 '13 at 09:39
  • 1
    I don't think so. Post that as another question and you'll get answers from people more familiar with the LLVM linker. – RyanR Oct 20 '13 at 13:23