Help understanding Threads

I’ve been trying to add support for threads with an image processing script, but I’m getting some unexpected results.
First I load some bitmapdata, then I setup a ThreadPool and add a worker function that will process different regions of the bitmapdata.
Then I queue each region.
With the final onComplete, as a test I tried reassembling all the tiles back into a single bitmap in their original positions, but every time I ran the script some tiles were placed in unexpected places, but it was different on each run. However the majority of tiles were correctly placed:

Limiting the number of threads in the ThreadPool to 1 resulted in no misplaced tiles.
I’m new to the idea of threads and terms like ‘race conditions’, I suspect I’ve probably demonstrated one in the code below:

var threadPool = new ThreadPool(0,8);
var i = 0;
var total = 0;

var bd = Assets.getBitmapData("test");
var tilesize = 128;
var grid_w = Math.ceil(bd.width / tilesize);
var grid_h = Math.ceil(bd.height / tilesize);
var tiles:Array<MetaTile> = [];

threadPool.doWork.add((p:{x:Int,y:Int})->{
    var x = p.x*tilesize;
    var y = p.y*tilesize;
    var s = tilesize;
    var sr = new Rectangle(x, y, s, s);
    var opaque_rect = get_nontransparent_pixels_rect(bd, sr); // returns the sub-region of non-transparent pixels within a region
    if (opaque_rect.width > 0 && opaque_rect.height > 0) {
        var sbd = new BitmapData(Math.ceil(opaque_rect.width), Math.ceil(opaque_rect.height), true, 0);
        sbd.copyPixels(bd, opaque_rect, new Point());
        var tile = {bd:sbd, x:x, y:y, ox:Std.int(x-opaque_rect.x), oy:Std.int(y-opaque_rect.y)};
        tiles.push(tile);
    }
    threadPool.sendComplete(null);
});
threadPool.onComplete.add((tile)->{
    i++;
    trace(i+"/"+total);
    if (i == total) {
        // now let's try and reconstruct the original image with the tiles...
        var temp_bd = new BitmapData(bd.width, bd.height, true, 0);
        for (tile in tiles) {
            temp_bd.copyPixels(tile.bd, tile.bd.rect, new Point(tile.x-tile.ox,tile.y-tile.oy));
        }
        save(temp_bd, "image.png");
    }
});

for (y in 0...grid_h) {
    for (x in 0...grid_w) {
        total++;
        threadPool.queue({x:x, y:y});
    }
}

It appears some regions are being repeatedly copied into the image.
Is this a limitation of OpenFl and the way bitmapdata is handled internally?
Or is it possible but my approach is wrong?

Possibly this problem:

Try to allocate a rectangle to each thread in advance.

Assuming the bug report is accurate that makes sense.

Allocating rectangles in advance though, I don’t think that’s an option.
Correct me if I’m wrong, but if the bug report is correct then the problem is not me calling new Rectangle() or new Matrix(), the problem is when OpenFl internally calls Rectangle.__pool.get() or Matrix.__pool.get() when I call one of the many OpenFl API methods.

However, casting doubt on that theory, the only explicit OpenFl calls I make inside the created thread are BitmapData.new, BitmapData.copyPixels and Image.getPixel32, and by setting breakpoints I discovered neither of them used ObjectPool.get.

So I’m still a bit confused…

If the threads interfere with each other, then in debug mode there will be the following messages:

1 Like