MXS: Running functions under a Struct in separate thread?

Hi there everyone,

I’m having problems with dotNET and multi threading (BackgroundWorker) inside a Struct.

I’m trying to create a general purpose Struct with various methods etc… to use in my workflow. I started with a method I need now, that takes a current maxFile and packs it externally in WinRAR via the Process class. I wanted to make this operation run in another thread so that I can still use Max while the process is being executed (packing certain large files takes time), however, I bumped into a problem I don’t really know how to deal with.

Here’s a snippet of the struct:

struct duberOps
(
	duberPath = @"C:\duber",
	sysPath = dotNetClass "System.IO.Path",
	process = dotNetClass "System.Diagnostics.Process",
	startInfo = dotNetClass "System.Diagnostics.ProcessStartInfo",
	MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker",
	
	fn runWinRARInBG sender ev =
	(
		for i = 1 to 10 do print ( "iter: " + (i as string) + " it works!" )
		return "SOMETHING"
	),
	
	fn compressCurrentSceneFile method:#rar =
	(
		-- the path to the rar.exe file
		local archiverPath = this.sysPath.Combine this.duberPath @"WinRAR\4.01_x64\Rar.exe"
		local process = dotNetObject this.process
		local startInfo = dotNetObject this.startInfo
		
		-- multithreading support
		this.MainThread.WorkerSupportsCancellation = true
		this.MainThread.WorkerReportsProgress = true
		dotNet.addEventHandler this.MainThread "DoWork" this.runWinRARInBG
		if this.MainThread.IsBusy do this.MainThread.CancelAsync()
		
		-- check whether the current file is saved to disk
		if maxFileName != "" or maxFilePath != "" do
		(
			local completeFileName = this.sysPath.Combine maxFilePath maxFileName
			if not this.MainThread.IsBusy do this.MainThread.RunWorkerAsync()
		)
	)	
)

duberOps = duberOps()

As you can see, I’m using a similar approach to Lonerobot’s multithreading example. But, when executing the duberOps.compressCurrentSceneFile() function that should trigger the runWinRARInBG() function in another thread via the dotnetobject “CSharpUtilities.SynchronizingBackgroundWorker” object, it only returns undefined. That’s it.

But when I add the dotNet eventHandler to a globally declared function outside that Struct, it runs it without problems.

Am I missing something here? Why isn’t the backgroundWorker triggering the function inside the same struct?

Thanks a lot for any tips in advance!

Made a few tweaks, and it is now working here, (the file path change shouldn’t be needful, just changed it to a local, existing path on my machine)

(
	struct duberOps
(
	duberPath = @"C:	est1",
	sysPath = dotNetClass "System.IO.Path",
	process = dotNetClass "System.Diagnostics.Process",
	startInfo = dotNetClass "System.Diagnostics.ProcessStartInfo",
	MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker",
	
	fn runWinRARInBG sender ev =
	(
		for i = 1 to 10 do print ( "iter: " + (i as string) + " it works!" )
		return "SOMETHING"
	),
	
	fn compressCurrentSceneFile method:#rar =
	(
		-- the path to the rar.exe file
		local archiverPath = this.sysPath.Combine this.duberPath @"WinRAR\4.01_x64\Rar.exe"
		local process = dotNetObject this.process
		local startInfo = dotNetObject this.startInfo
		
		-- multithreading support
		this.MainThread.WorkerSupportsCancellation = true
		this.MainThread.WorkerReportsProgress = true
		dotNet.addEventHandler this.MainThread "DoWork" this.runWinRARInBG
		if this.MainThread.IsBusy do this.MainThread.CancelAsync()
		
		-- check whether the current file is saved to disk
		if maxFileName != "" or maxFilePath != "" do
		(
			local completeFileName = this.sysPath.Combine maxFilePath maxFileName
			if not this.MainThread.IsBusy do this.MainThread.RunWorkerAsync()
		)
	)	
)

theStructInstance = duberOps()
theStructInstance.runWinRARInBG "" ""

)

…which printed out in the MAXscript Listener,


"iter: 1 it works!"
"iter: 2 it works!"
"iter: 3 it works!"
"iter: 4 it works!"
"iter: 5 it works!"
"iter: 6 it works!"
"iter: 7 it works!"
"iter: 8 it works!"
"iter: 9 it works!"
"iter: 10 it works!"
"SOMETHING"

Thanks j83, but you’re actually calling the function directly :slight_smile: which is something I don’t want/need.

I need the function to be run from within the compressCurrentSceneFile in a separate thread. That’s what doesn’t work. :wink:

I actually managed to make it run, but I had to include the actual MainThread object in the function itself, not outside.

Ah, sorry.

Have you tried using the unary scope operator? (::foo())

I actually managed to make it run, but I had to include the actual MainThread object in the function itself, not outside.

Good :slight_smile:

I didn’t look too closely but it sounds like it could be a garbage collection issue. Try setting the garbage collector (lifetime, or something like that?). Or you can assign things globally to make sure they aren’t garbage collected, like I think you did.

The red flags of garbage collection issues in Max/.NET (where one GC is interferring with the other) are semi-arbitrary crashes (when something is GC’ed “illegally”), and events disappearing.

Hmm… that sounds like a possible problem, Rob. Thanks for pointing that out. I’ll see what I can do about it.