lime icon indicating copy to clipboard operation
lime copied to clipboard

Make `ThreadPool` support `Deque` again.

Open player-03 opened this issue 8 months ago • 18 comments

The 8.2.0 version of ThreadPool uses the main thread to schedule threads, due to restrictions in HTML5. I figured it was easiest to make that behavior consistent across platforms, and it was worth a bit of extra overhead to get HTML5 web worker support. But users have complained about performance when running lots of small jobs (a use case I hadn't considered), so I finally got around to bringing Deque back.

This PR uses a flag (lime_threads_deque) to determine whether to use Deque. It's enabled by default on native targets, but you can comment it out for testing purposes. I'm hoping that we can do something fancy with HTML5 to make the equivalent of a Deque, and then remove the flag. That's why I made a new flag instead of just using #if !html5: it will make the transition easier in the future.

I've tested this PR in my own use cases, making sure all flag combinations work. Single-threaded, multi-threaded, with and without Deque, on native and HTML5. (Except I didn't test Deque on HTML5, because that isn't available.) But this is still a big rewrite, and it would be easy to miss something, so I'd appreciate if other users could test their use cases too.

Fixes #1895, supersedes #1837.

player-03 avatar Jun 26 '25 05:06 player-03

I tested the HTML5 target of the game project and it performed well, with speed restored to before.

rainyt avatar Jun 26 '25 07:06 rainyt

@barisyild Just fixed several bugs, including most of what you reported. Not sure about the lag; if it's still there you'll have to provide a sample.

player-03 avatar Jun 27 '25 02:06 player-03

@barisyild Just fixed several bugs, including most of what you reported. Not sure about the lag; if it's still there you'll have to provide a sample.

Bug still exists.

Assets.loadLibrary ("nolibrarywiththisname").onError(function (lib:AssetLibrary) {
	Sys.println("Error loading library!");
}).onComplete (function (lib:AssetLibrary) {
	Sys.println("Library loaded successfully!");
});

Old ThreadPool: Error loading library! New ThreadPool: nothing

barisyild avatar Jun 27 '25 12:06 barisyild

That's fixed; make sure you pull the latest changes.

(I've tested on Linux, in C++, Neko, HL, and HTML5. For HTML5 I used trace() instead of println().)

player-03 avatar Jun 27 '25 16:06 player-03

That's fixed; make sure you pull the latest changes.

(I've tested on Linux, in C++, Neko, HL, and HTML5. For HTML5 I used trace() instead of println().)

It doesn't work for me either, by the way I use it with lime 8.2.2 by just changing the files.

https://github.com/openfl/lime/pull/1958/files

barisyild avatar Jun 27 '25 17:06 barisyild

Bug

Code

public function new() {
  super();
  Sys.println("Started!");
  Assets.loadLibrary ("nolibrarywiththisname").onError(function (lib:AssetLibrary) {
	  Sys.println("Error loading library!");
  }).onComplete (function (lib:AssetLibrary) {
	  Sys.println("Library loaded successfully!");
  });
}

Output:

Started!

Working

Code

public function new() {
  super();
  Sys.println("Started!");
  Assets.loadLibrary ("nolibrarywiththisname").onError(function (lib:AssetLibrary) {
	  Sys.println("Error loading library!");
  }).onComplete (function (lib:AssetLibrary) {
	  Sys.println("Library loaded successfully!");
  });
  
  new Future<Void>(() -> Sys.println("1"));
}

Output:

Started!
1
Error loading library!

barisyild avatar Jun 27 '25 18:06 barisyild

public function new() {
  Sys.println("Started!");
  Assets.loadLibrary ("nolibrarywiththisname").onError(function (lib:AssetLibrary) {
     Sys.println("Error loading library!");
  }).onComplete (function (lib:AssetLibrary) {
     Sys.println("Library loaded successfully!");
  });
  
  new Future<Void>(() -> Sys.println("1"));
}

That looks exactly like the first error I fixed. The pool would shut down a bit too soon, so the error wouldn't be printed until the pool restarted.

Except that can't be what's going on here. Assets.loadLibrary() uses a different pool than new Future(), and starting a new pool does not make the old pool check its messages.

Perhaps the entire app is shutting down too soon in your case? What if you try haxe.Timer.delay(() -> Sys.println("1"), 1000); instead of new Future()?

player-03 avatar Jun 27 '25 18:06 player-03

That looks exactly like the first error I fixed. The pool would shut down a bit too soon, so the error wouldn't be printed until the pool restarted.

Except that can't be what's going on here. Assets.loadLibrary() uses a different pool than new Future(), and starting a new pool does not make the old pool check its messages.

Perhaps the entire app is shutting down too soon in your case? What if you try haxe.Timer.delay(() -> Sys.println("1"), 1000); instead of new Future()?

No, the application does not close. Can you try reproduce?

barisyild avatar Jun 27 '25 18:06 barisyild

As stated, I've tried this plenty of times, on plenty of targets, and cannot reproduce the error.

Another thing: your new() function doesn't seem to call super(). Are you running this in a Lime or OpenFL app? Because it very much relies on Lime's event loop, and it's not going to work outside of that.

player-03 avatar Jun 27 '25 18:06 player-03

As stated, I've tried this plenty of times, on plenty of targets, and cannot reproduce the error.

Another thing: your new() function doesn't seem to call super(). Are you running this in a Lime or OpenFL app? Because it very much relies on Lime's event loop, and it's not going to work outside of that.

OpenFL, can you try this code on main class.

class Main extends Sprite
{
  public function new() {
    super();
    Sys.println("Started!");
    Assets.loadLibrary ("nolibrarywiththisname").onError(function (lib:AssetLibrary) {
	    Sys.println("Error loading library!");
    }).onComplete (function (lib:AssetLibrary) {
	    Sys.println("Library loaded successfully!");
    });
  }
}

barisyild avatar Jun 27 '25 18:06 barisyild

You know, that first lib:AssetLibrary should be error:String instead. I always fixed it on my end without bothering to mention it, but that might explain why mine works. When I copy-pasted your code, the app crashed with an error. When I changed it to this, it worked successfully, printing "Started!" and then "Error loading library!"

package;

import openfl.display.Sprite;
import openfl.Assets;
import openfl.utils.AssetLibrary;

class Main extends Sprite
{
  public function new() {
    super();
    Sys.println("Started!");
    Assets.loadLibrary ("nolibrarywiththisname").onError(function (error:String) {
	    Sys.println("Error loading library!");
    }).onComplete (function (lib:AssetLibrary) {
	    Sys.println("Library loaded successfully!");
    });
  }
}

Edit: or maybe it's specifically HL that cares about the difference. Neko and C++ seem to accept it just fine. How about you try in HL?

player-03 avatar Jun 27 '25 18:06 player-03

You know, that first lib:AssetLibrary should be error:String instead. I always fixed it on my end without bothering to mention it, but that might explain why mine works. When I copy-pasted your code, the app crashed with an error. When I changed it to this, it worked successfully, printing "Started!" and then "Error loading library!"

package;

import openfl.display.Sprite;
import openfl.Assets;
import openfl.utils.AssetLibrary;

class Main extends Sprite
{
  public function new() {
    super();
    Sys.println("Started!");
    Assets.loadLibrary ("nolibrarywiththisname").onError(function (error:String) {
	    Sys.println("Error loading library!");
    }).onComplete (function (lib:AssetLibrary) {
	    Sys.println("Library loaded successfully!");
    });
  }
}

Edit: or maybe it's specifically HL that cares about the difference. Neko and C++ seem to accept it just fine. How about you try in HL?

I'm trying on hxcpp, weird.

barisyild avatar Jun 27 '25 18:06 barisyild

HL does tend to be more picky about types. Might as well give it a try, just to see if it behaves differently in other ways.

Aside from that, try pulling from Git, to make sure you aren't missing anything.

git remote add player-03 https://github.com/player-03/lime.git
git fetch player-03
git checkout --track player-03/ThreadPool_Deque

player-03 avatar Jun 27 '25 19:06 player-03

Update: we debugged this on Discord. It turned out to be a timing issue: there was a brief window where ThreadPool could incorrectly remove its update listener, and it never happened on my machine, but did on his.

Thanks to everyone who's tested so far. This is why it matters.

player-03 avatar Jun 27 '25 20:06 player-03

I tried building my threadpool example with this PR and it's failing with :

Error: SDL_hidapi_steamdeck.c c1: fatal error C1083: Cannot open source file: './lib/sdl/src/joystick/hidapi/SDL_hidapi_steamdeck.c': No such file or directory

I'm on Win 10 Haxe 4.3.6 and I pulled this branch with this command into a local haxelib in the project

haxelib git lime https://github.com/player-03/lime.git ThreadPool_Deque -debug

Anything I'm missing, doing incorrectly ?

47rooks avatar Jun 28 '25 15:06 47rooks

Looks like it's trying to rebuild the NDLLs, which is expected when installing from Git. You can often copy NDLLs from an existing Lime install (compatibility depends on how much changed between the two versions), or you can get them from the CI run for that commit (always compatible).

Or you can recompile them yourself. For that, you need to get the submodules using git submodule update --init, and you'll need the prerequisites listed here.

player-03 avatar Jun 28 '25 16:06 player-03

Ok, that was it. Thanx - I think I've done this before but not for a while evidently. Good news is my 10 thread fibonacci test program ran as expected with this change, in both cpp and hl targets. Next is to try my flixel parallel loader. And that also worked on cpp and HL.

47rooks avatar Jun 28 '25 20:06 47rooks

Great, thanks for testing!

player-03 avatar Jun 28 '25 21:06 player-03

I confirm that this fixes issue #1895.

joshtynjala avatar Jul 07 '25 19:07 joshtynjala

Great!

Since no one else has reported any issues, and there has been a big push for this, I think it's time.

player-03 avatar Jul 07 '25 20:07 player-03

There is still issue in my tests, but it's incredibly rare.

Could the problem be due to lack of mutex?

It seems like other threads are accessing the __queuedExitEvents value.

barisyild avatar Jul 14 '25 10:07 barisyild

It seems like other threads are accessing the __queuedExitEvents value.

__queuedExitEvents and similar values are only accessed from the main thread. What makes you think otherwise?

player-03 avatar Jul 14 '25 17:07 player-03

Sorry, Recently, while testing Android, I encountered some new issues. I am using the 8.3.0dev branch, which includes this change. On cpp, URLLoader will perform loading without any successful or failed callbacks. How should I proceed with the next steps of testing and inspection?

rainyt avatar Aug 14 '25 07:08 rainyt

2025-08-14 16:37:02.743 22553-22798/? I/trace: lime/system/ThreadPool.hx:398: run,{ uri => manifest/default.json, instance => NativeHTTPRequest },null
2025-08-14 16:37:02.750 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 0,WORK,null,idleThreads,1,activeThreads,0
2025-08-14 16:37:02.777 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 0,PROGRESS,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.777 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 0,COMPLETE,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.804 22553-22798/? I/trace: lime/system/ThreadPool.hx:398: run,Object,null
2025-08-14 16:37:02.805 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: null,IDLE,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.811 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 1,WORK,null,idleThreads,1,activeThreads,0
2025-08-14 16:37:02.811 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 1,COMPLETE,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.812 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: null,IDLE,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.827 22553-22798/? I/trace: lime/system/ThreadPool.hx:398: run,CURLMulti,null
2025-08-14 16:37:02.833 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 2,WORK,null,idleThreads,1,activeThreads,0
2025-08-14 16:37:02.969 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 2,PROGRESS,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.969 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 2,PROGRESS,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.971 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: 2,COMPLETE,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.971 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: null,IDLE,null,idleThreads,0,activeThreads,1
2025-08-14 16:37:02.986 22553-22798/? I/trace: lime/system/ThreadPool.hx:398: run,CURLMulti,null
2025-08-14 16:37:02.986 22553-22798/? I/trace: lime/system/ThreadPool.hx:459: null,EXIT,null,idleThreads,1,activeThreads,0

Is this helpful?

rainyt avatar Aug 14 '25 08:08 rainyt

There must still be a problem, I fixed it after rolling back the code. I don't know how to solve it.

rainyt avatar Aug 14 '25 09:08 rainyt