Thread issue

I’ve got an application that is doing a bit of file copying, renaming and sorting.
As the number of files I’m dealing with has increased I decided to put the methods that handle that work into a separate thread so that the UI doesn’t lock up and so that I can incorporate a progress indicator, some listbox messages, etc.

I’ve set up the thread and started a timer to monitor a ‘threadIsDone’ flag and handle the progress updating code.

The issue I’m having, though, is that the interface still locks up (beach balls) until completion of the thread. I don’t know what going on. I’ve set up this sort of thread/timer construction many times before in other applications and each time it’s worked as I would expect. Not this time, though.

Xojo’s threads are co-operative. Look at the new “Workers” class, as this will give you what you want.

Once you’ve adapted your code to the new paradigm, build the app to see “Workers” in action as they still use threads in the debug build.

I considered the worker class but thought that a Xojo thread might be more appropriate for this case, since what I’m trying to do is simply separate the bigger, resource heavy work from the UI.

Am I thinking about it wrong?

Thanks!

Co-operative threading is where the single core / processor is shared between multiple threads from one application. So when a function takes a long time, it prevents any other co-operative threads (from that application) from getting any CPU time, hence why it continues to lock up.

The worker is essentially a separate application inside of your main application. The OS can then manage this in a way that doesn’t perceivably effect your main/GUI application, keeping it responsive and smooth.

While App Wrapper 4 doesn’t use Xojo’s “Workers” (because of a design decision I made earlier on in the year), it uses the same principle. Which enables App Wrapper to process applications faster and the GUI / Main application to remain responsive.

I hope that this makes sense, I know I am not the greatest at explaining things.

If you can forgo progress while copying files, I can share a Mac specific way to copy files while keeping the GUI responsive. You can still provide feedback as to when a file was copied, but you won’t be able to provide how much of a file has been copied.

Thanks Sam. I do understand your explanation.
However I thought that’s what a Xojo thread + timer configuration did: force time intensive tasks to run “in the background” so that the UI could remain somewhat more responsive. Is it not working as I am expecting because of the nature of the process I’m running in the thread? (copying, sorting and renaming several hundred 2-5 MB files?)

(And yes, I would appreciate any tips on Mac specific file copying that would leave the UI responsive – thanks.)
~
cheers

If the calls you make on the background thread take a long time, eg read in all of a text file, it will lock up all other threads, including the main one. Making lots of little calls to give the other threads time is important as well.

Unfortunately, file io blocks in Xojo even when running in a different thread. If you search feedback you should find requests for non blocking / async file io.

If you have the MBS plugins they include functions that support non blocking file copying.

I imagine the new worker class could be used but that seems such an awful solution to a simple problem.

I agree with @kevin_g, use the MBS file copying methods. I have a backup app that copies files in a thread and it too beachballs if the files are too large when copied.

Thanks for recommending MBS – I need to get in the habit of checking his plugins for solutions.

Would that be the CopyFileMBS.copyTo method?

Trying to implement it (with f and renamedFolder being folderItems and j and flags being integers)
j = CopyFileMBS.CopyFile(f, renamedFolder, flags)
I get the message: “There is more than one method with this name but this does not match any of the available signatures.”

So I’ve implemented the CopyFileMBS.copyTo method successfully but it’s still locking up the UI – even if I run that code in another thread. (the copy operation is acting on several hundred 2-5 MB files.)

Is my only recourse to implement it with the worker class?

you use

t.YieldTicks=6 // only use 1/10th of a second

in your code?

@Christian_Schmitz, no, I didn’t.
Will I need to add that to the thread that contains the copy code in order for that thread to allow processing cycles for the UI thread? Where would I put it? I imagine it would be placed in a loop so that I could devote some time to other threads but the majority of the work is being done in the recursive copyFile method.

var c as new CopyFileMBS
var j, flags as integer
flags = c.kFlagsRecursive + c.kFlagsAll

for each f as FolderItem in dayFolder.child("1_Original").children
  if f.IsFolder then
    if not renamedFolder.child(f.name).Exists then
      j = c.CopyFile(f, renamedFolder, flags)
    end if
  end if

So this is all theoretical and untested, except I use the ditto command to copy files in my apps.

This will require a change in your designs to make the copy event driven. I would suggest creating an array of pairs which contain the source folderitem and the destination folderitem.

Add a shell class to the window, along with the supporting filesToCopy(-1) as pair array.
Set the shell class to be Asynchronous.

Once the array has been populated, call the following method to start the copy process.

declare sub copyNextFile()
  if ubound( filesToCopy ) > -1 then
    // --- Take the first item from the array and then remove it
    Dim cFile as pair = filesToCopy( 0 )
    filesToCopy.remove 0

    // --- Update UI here

    // --- Now perform the copy
    shell.execute "/usr/bin/ditto " + folderitem( cFile.left ).shellPath + " " + folderitem( cFile.right ).shellPath

  else
    // --- File copy completed

  end if
end sub

Add the completed event to the shell class.

event completed
  if me.errorcode = 0 then
    // --- No error, so start the next copy
    copyNextFile

  else
    // --- Display error and abort the process

  end if
end event

I think that should do it.

var c as new CopyFileMBS
c.YieldTicks = 6

just put the value there after the new line.