Difference between DART Isolate and Thread (Java,C#) - dart

For me The DART Isolate looks like a Thread (Java/C#) with a different terminology. In which aspect Isolate differs from a Thread?

Threads use shared memory, isolates don't.
For example, the following pseudocode in Java/C#
class MyClass {
static int count = 0;
}
// Thread 1:
MyClass.count++;
print(MyClass.count); // 1;
// Thread 2:
MyClass.count++;
print(MyClass.count); // 2;
This also runs the risk of the shared memory being modified simultaneously by both threads.
Whereas in Dart,
class MyClass {
static int count = 0;
}
// Isolate 1:
MyClass.count++;
print(MyClass.count); // 1;
// Isolate 2:
MyClass.count++;
print(MyClass.count); // 1;
Isolates are isolated from each other. The only way to communicate between them is to pass messages. One isolate can listen for callbacks from the other.
Check out the docs here including the "isolate concepts" section.

Related

How does Vala handle reference counting with multithreading?

As far as I understand, in multithreaded environment reference counting should be performed with locking to ensure all threads see the same snapshot of memory. But locking slows down perfomance. How does Vala solve this problem?
Reference counting is mostly handled in GObject (for GLib.Object-derived types), which in turn uses the Atomic Operations in GLib. Atomics are a tricky subject; if you want to get into details a good place to start is Herb Sutter's Atomic Weapons talk from a few years ago. I would recommend watching the videos even if you're never going to put them to use (and 99.9% of programmers should never put them to use) because it will give you a much better understanding of how computers really work.
The name "atomics" can be a bit misleading; it's not really about atomicicity, though that's part of it. The operations are atomic in the sense that the change is either made in its entirety or not at all, which is vital, but the more interesting part is that atomics act as barriers which prevent the compiler from re-ordering operations across the barrier. Herb Sutter's talk goes into a lot of detail about this which I'm not going to repeat here.
For example, think about a simple unprotected reference counter:
typedef struct {
int reference_count = 0;
} Foo;
Foo* foo_create(void) {
Foo* foo = malloc(sizeof(Foo));
foo->reference_count = 1;
}
void ref(Foo* foo) {
++(foo->reference_count);
}
void unref(Foo* foo) {
if (--(foo->reference_count) == 0) {
free(foo);
}
}
I'm going to assume you can see the problems with leaving this unprotected because I'm writing a SO post not a book.
The specific atomic operation we're interested in is compare-and-swap (CAS), which basically provides the ability to perform this operation safely:
bool cas(int* value, int* expected, int desired) {
if (*value == *expected) {
*value = desired;
return true;
} else {
return false;
}
}
Using this, we would change our refcounting implementation above to something like:
typedef struct {
int reference_count = 0;
} Foo;
Foo* foo_create(void) {
Foo* foo = malloc(sizeof(Foo));
/* No atomics needed, we haven't made the value public yet */
foo->reference_count = 1;
}
void ref(Foo* foo) {
int old_refcount;
int new_refcount;
do {
current_refcount = foo->reference_count;
new_refcount = current_refcount + 1;
} while (!cas (&(foo->reference_count), &old_refcount, new_refcount))
}
void unref(Foo* foo) {
int old_refcount;
int new_refcount;
do {
current_refcount = foo->reference_count;
new_refcount = current_refcount - 1;
} while (!cas (&(foo->reference_count), &old_refcount, new_refcount));
if (new_refcount == 0) {
free(foo);
} else if (new_recount < 0) {
// Double-free bug, code should not be reached!
}
}
But locking slows down perfomance.
So do atomics. A lot. But also a lot less than a higher-level lock would. For one thing, if you were working with a mutex what you are doing would basically be:
Acquire the lock.
Perform the operation.
Release the lock.
With atomics, we're basically begging forgiveness instead of asking permission:
Attempt to perform the operation.
Then we just look to see whether the operation was successful (i.e., if cas() returned true).
The operation is also a lot smaller and faster; with a mutext, you would probably acquire the lock then read the current value, increment / decrement it, then release the lock. With atomics, the CAS operation gets wrapped up in a single CPU instruction.
The CPU still has to deal with cache coherency by making sure that next time any other core (a bit oversimplified since even within a core there are multiple caches) asks to read the data they are presented with the new data. In other words, atomic reference counting is bad for performance, but it's a lot less bad than a mutex. Frankly, if you want reference counting instead of tracing garbage collection atomics are pretty much your least-bad option.

The real use of Autorelease pool [duplicate]

On page 17 of this WWDC14 presentation, it says
Working with Objective-C? Still have to manage autorelease pools
autoreleasepool { /* code */ }
What does that mean? Does it mean that if my code base doesn't have any Objective-C files, autoreleasepool {} is unnecessary?
In an answer of a related question, there is an example where autoreleasepool can be useful:
- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
#autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(#"number = %p", number);
}
}
}
}
If the code above gets translated into Swift with autoreleasepool dropped, will Swift be smart enough to know that the number variable should be released after the first } (like some other languages does)?
The autoreleasepool pattern is used in Swift when returning autorelease objects (created by either your Objective-C code or using Cocoa classes). The autorelease pattern in Swift functions much like it does in Objective-C. For example, consider this Swift rendition of your method (instantiating NSImage/UIImage objects):
func useManyImages() {
let filename = pathForResourceInBundle
for _ in 0 ..< 5 {
autoreleasepool {
for _ in 0 ..< 1000 {
let image = NSImage(contentsOfFile: filename)
}
}
}
}
If you run this in Instruments, you'll see an allocations graph with 5 small hills (because outer for-loop), like the following:
But if you do it without the autorelease pool, you'll see that peak memory usage is higher:
The autoreleasepool allows you to explicitly manage when autorelease objects are deallocated in Swift, just like you were able to in Objective-C.
Note: When dealing with Swift native objects, you generally will not receive autorelease objects. This is why the presentation mentioned the caveat about only needing this when "working with Objective-C", though I wish Apple was more clear on this point. But if you're dealing with Objective-C objects (including Cocoa classes), they may be autorelease objects, in which case this Swift rendition of the Objective-C #autoreleasepool pattern is still useful.
If you would use it in the equivalent Objective-C code, then you would use it in Swift.
will Swift be smart enough to know that the number variable should be
released after the first }
Only if Objective-C does. Both operate along the Cocoa memory management rules.
Of course ARC knows that number goes out of scope at the end of that iteration of the loop, and if it retained it, it will release it there. However, that does not tell you whether the object was autoreleased, because -[NSNumber numberWithInt:] may or may not have returned an autoreleased instance. There is no way you can know, because you don't have access to the source of -[NSNumber numberWithInt:].
#autoreleasepool can be used in Objective-C and Swift code to guarantee working with Objective-C code which relies on autorelease
[Under the hood]

make a simple NSInteger counter thread safe

I define a NSInteger counter and updated its value in a callback like the following code shows (callback is in another thread):
-(void) myFunc {
NSLog(#"initialise counter...");
// I try to use volatile to make it thread safe
__block volatile NSInteger counter = 0;
[self addObserver:myObserver withCallback:^{
// this is in another thread
counter += 1;
NSLog(#"counter = %d", counter);
}];
}
I use volatile keyword to make the counter thread safe, it is accessed in a callback block which belongs to another thread.
When I invoke myFunc two times:
// 1st time call
[self myFunc];
// 2nd time call
[self myFunc];
the output is like this:
initialise counter...
counter = 1;
counter = 2;
counter = 3;
counter = 4;
counter = 1; // weird
initialise counter...
counter = 2; // weird
counter = 3;
counter = 1; // weird
counter = 4;
It looks like the 2nd time call produce a counter with wrong initial value, and the output before counter=4 is counter=1 which is also weird.
Is it because my code is not thread safe even with volatile keyword? If so, how to make my counter thread safe? If it is thread safe, why I get weird output?
For the simple case of an atomic counter, GCD is overkill. Use the OSAtomic functions:
-(void) myFunc {
static int64_t counter;
[self addObserver:myObserver withCallback:^{
// this is in another thread
int64_t my_value = OSAtomicIncrement64Barrier(&counter);
NSLog(#"counter = %d", my_value);
}];
}
Note that the code logs the result of the increment function rather than the static variable. The result gives you the atomic result of your specific operation. Using the static variable would give you a snapshot of the counter that's not atomic with respect to your increment operation.
First of all using a local variable is corrupted. It will be removed from stack, when the function returns. Therefore the block copies the variable's value (capture) when the block definition is executed (counter = 0) and works on the copy.
If you have a shared resource as the counter is, you have to put accesses to it into a block.
// global
dispatch_queue_t counterQueue;
int counter;
// initialize
counterQueue = dispatch_queue_create( "com.yourname.counterQueue", DISPATCH_QUEUE_SERIAL);
counter = 0;
// Whenever you read or write to counter
dispatch_async( counterQueue,
^{
counter++;
NSLog( #"%d", counter" );
}
// or
int lastValue;
dispatch_sync( counterQueue,
^{
lastValue = counter;
}
// Do something with it.
There are lots of things wrong with your code. It looks like you're calling myFunc repeatedly. Each time you do, it creates a new instance of the counter.
Make your counter an instance variable or app-wide global.
A simple way to make incrementing (and logging) the counter thread-safe is to make the body of the observer use dispatch_async(dispatch_get_main_queue()<your code here>). That way the code that messes with the counter always runs on the main thread, even if it's called from other threads. This isn't the most performant way to handle it, but it's easy.
Otherwise you're going to need to use locks or some other concurrency technique. That requires a strong understanding of thread safety, which your post, frankly, shows that you don't have. (Not to be mean, it's one of the more difficult subjects in computing.)
EDIT:
As Avi points out in his comment, using the main queue to manage the counter would cause the other threads to block waiting on the main thread, and is not a very good solution. (It would work, but would take away just about all the performance benefit of using multiple threads)
It would be better to set up a single serial queue and make that a lazily loaded property of the object that manages this counter, protected with dispatch_once(). However, I don't have enough coffee on-board to write out that code in a forum post.

Debugging trivial functions in Objective-C

Recently I just started working on iOS game programming, and I find several things confusing. (FYI, I am working on a simple game with code provided on makegamewith.us)
First, I just found out that only the main function is executed. By this I mean we use the main function to activate iOS simulator, so that we will be able to load our game. Then I realize that breakpoints only work in main functions. As I put breakpoints in other files (such as creature.m, a game component), despite that I use a function to create creature objects in the game, Xcode won't stop at that function. The iOS simulator will be called, and then the game will be automatically loaded.
So here is the question: how can I debug then?
I assume that function is called when I run the game, but Xcode just ignores any other function in other files except the main function in main.m.
Also, I encountered several "Couldn't find member variable" situations. I wonder how to prevent this from happening. The whole sprite builder publishing to Xcode thing appears blurry. I would appreciate if someone can explain how the whole thing works.
Update:
I realize that I didn't explicitly call any of the functions I have in other files (for instance, Grid.m as shown below). By main function, I mean the int main function in main.m. So the problem might possibly be that I didn't explicitly call that function in main? (but I think what main.m is responsible for is launching the program.)
In main.m:
int main(int argc, char *argv[]) {
#autoreleasepool //if I put a breakpoint here this will definitely work
{
int retVal = UIApplicationMain(argc, argv, nil, #"AppController");
return retVal;
}
}
Grid.m
#import "Grid.h"
#import "Creature.h"
// these are variables that cannot be changed
static const int GRID_ROWS = 8;
static const int GRID_COLUMNS = 10;
#implementation Grid {
NSMutableArray *_gridArray;
float _cellWidth;
float _cellHeight;
}
- (void)onEnter
{
[super onEnter];
[self setupGrid];
// accept touches on the grid
self.userInteractionEnabled = YES;
}
- (void)setupGrid //****if I put breakpoint here, it doesn't work****
{
// divide the grid's size by the number of columns/rows to figure out the right width and height of each cell
_cellWidth = self.contentSize.width / GRID_COLUMNS;
_cellHeight = self.contentSize.height / GRID_ROWS;
float x = 0;
float y = 0;
// initialize the array as a blank NSMutableArray
_gridArray = [NSMutableArray array];
// initialize Creatures
for (int i = 0; i < GRID_ROWS; i++) {
// this is how you create two dimensional arrays in Objective-C. You put arrays into arrays.
_gridArray[i] = [NSMutableArray array];
x = 0;
for (int j = 0; j < GRID_COLUMNS; j++) {
Creature *creature = [[Creature alloc] initCreature];
creature.anchorPoint = ccp(0, 0);
creature.position = ccp(x, y);
[self addChild:creature];
// this is shorthand to access an array inside an array
_gridArray[i][j] = creature;
// make creatures visible to test this method, remove this once we know we have filled the grid properly
creature.isAlive = YES;
x+=_cellWidth;
}
y += _cellHeight;
}
}
#end
Take a look at your main() function -- almost the only thing it does is to call UIApplicationMain(). This is true for any iOS application. So the real work is being done by UIApplicationMain(), and we should find out about that. Here's the description from the docs:
This function instantiates the application object from the principal
class and instantiates the delegate (if any) from the given class and
sets the delegate for the application. It also sets up the main event
loop, including the application’s run loop, and begins processing
events. If the application’s Info.plist file specifies a main nib file
to be loaded, by including the NSMainNibFile key and a valid nib file
name for the value, this function loads that nib file.
So if there's a problem with your app, it's likely related to the application delegate, your AppController class. Set a breakpoint in your -[AppController application:willFinishLaunchingWithOptions:] method -- that's what the application itself will call on its delegate when the app is ready to run. Your UIApplicationMain() call in main() looks OK, so the debugger should hit a breakpoint in your ...didFinishLaunching... method. Step through that method and make sure that you're setting up a window, setting a root view controller, etc. If you're not sure what needs to happen, try creating a new single-view project and looking at the code that's provided there.

What is the parameter that #synchronized() takes

I know what #synchronized() does, but...
sometimes we have:
1- #synchronized(self)
2- #synchronized([MyClass class])
3- #synchrinized(myObj)
What is the difference, and what is the parameter I should pass to this block ?
From the documentation:
The object passed to the #synchronized directive is a unique
identifier used to distinguish the protected block. If you execute the
preceding method in two different threads, passing a different object
for the anObj parameter on each thread, each would take its lock and
continue processing without being blocked by the other. If you pass
the same object in both cases, however, one of the threads would
acquire the lock first and the other would block until the first
thread completed the critical section.
So it depends on what you want to protect from being executed simultaneously,
and there are applications for all three cases.
For example, in
-(void)addToMyArray1:(id)obj
{
#synchronized(self) {
[self.myArray1 addObject:obj];
}
}
-(void)addToMyArray2:(id)obj
{
#synchronized(self) {
[self.myArray2 addObject:obj];
}
}
both #synchronized blocks cannot be executed simultaneously by two threads calling
the method on the same instance (self), thus protecting simultaneous access to the
arrays from different threads.
But it also prevents the block from the first method
to be executed simultaneously executed with the block from the second method, because they
use the same lock self. Therefore, for more fine-grained locking, you could use
different locks:
-(void)addToMyArray1:(id)obj
{
#synchronized(self.myArray1) {
[self.myArray1 addObject:obj];
}
}
-(void)addToMyArray2:(id)obj
{
#synchronized(self.myArray2) {
[self.myArray2 addObject:obj];
}
}
Now the simultaneous access to self.myArray1 and self.myArray2 from different threads
is still protected, but independently of each other.
A lock on the class can be used to protect access to a global variable.
This is just a trivial example for demonstration purposes:
static int numberOfInstances = 0;
-(id)init
{
self = [super init];
if (self) {
#synchronized([self class]) {
numberOfInstances++;
}
}
}
#synchronized should have the same object passed each time. So #synchronized(self) would work best.

Resources