-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix data races, #3829, #3830, #3936, #3937 #4819
Conversation
Been waiting for a release that includes beta testing to make these significant changes. Lots here for reviewers to criticize. Before the changes in this PR
So we should be careful about using Rather than add locks around lots of properties this PR changes Does anyone know why the re-enabling of the OSD which is disabled until after file loading is not re-enabled in the As |
I'm a lot more doubtful about the need for Are you familiar with
The But |
Good to hear that switching from Using dispatchPrecondition is a good tip. I will think about where that could be used. I was expecting the odd 0.2 second delay and the code not being in the |
Rebased with |
Rebased with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job! Generally looks good to me.
Another concern is that now lots of functions in PlayerCore
are meant to be called on the main thread. We should at least add some comments to alert future developers.
iina/MPVController.swift
Outdated
mpv_hook_continue(self.mpv, hookID) | ||
} | ||
let hook = $hooks.withLock { $0[userData] } | ||
guard let hook = hook else { break } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can write
guard let hook = $hooks.withLock({ $0[userData] }) else { break }
iina/MPVController.swift
Outdated
player.refreshEdrMode() | ||
if #available(macOS 10.15, *) { | ||
DispatchQueue.main.async { self.player.refreshEdrMode() } | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are now targeting 10.15. Maybe you added it back accidentally during rebase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I probably missed that during the rebase. Will fix.
iina/MPVController.swift
Outdated
player.syncUI(.chapterList) | ||
player.postNotification(.iinaMediaTitleChanged) | ||
DispatchQueue.main.async { [self] in | ||
player.info.chapter = Int(getInt(MPVProperty.chapter)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This getInt()
needs to be moved outside
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused. Why can't getInt
be called on the main thread?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's mpv_get_property()
, and we want it to be on the com.colliderli.iina.controller
thread. If it's on the main thread, then for example, mpv_destroy()
is on the com.colliderli.iina.controller
thread, and it will cause another potential crash due to race condition when closing the window.
Lines 970 to 975 in 1ebe0f4
} else { | |
RunLoop.main.perform(#selector(self.terminateApplication), target: self, | |
argument: nil, order: Int.min, modes: [.common]) | |
} | |
} else { | |
mpv_destroy(mpv) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix, but we do call methods like getInt
outside of MPVController
from code I'm sure is running on the main thread. I will think on how to better coordinate this. I have seen mpv
emit some events during shutdown that it really shouldn't be emitting. I've been worried that we let q
through to mpv
causing mpv
to shutdown "behind" IINA's back although I've not been able to trigger a crash in testing that. The gets
are a problem because they do not return optional values. If they did it would be easy to detect shutdown and return nil
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but these ones are perhaps much easier to trigger because it's in handleEvent
. We can't say exactly that "mpv is emitting events during shutdown", because this block is queued on the main thread and executed later than the event, after some unknown delay. If mpv shuts down right after emitting the event, there will be a crash.
The gets are a problem because they do not return optional values. If they did it would be easy to detect shutdown and return nil.
If we are going to add the check, I think we can just return 0. If mpv is already terminated then the value makes little sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brain wasn't remembering this PR last night, it has been a while since I coded it. I remember thinking having the get methods return some random value seemed a bit wrong. So I decided it would be better to coordinate shutdown at a higher level. I have been slowly refactoring MPVController to call "listener" methods in PlayerCore. Those methods have the ability to check if shutdown is in progress and if so, drop the event. I need to refactor the code you pointed out into additional "listener" methods in PlayerCore that check the state and avoid calling getInt
.
I'm used to a layered design where the bottom layer classes know very little about the higher level code. I was expecting we'd have a class that was focused solely on providing a Swift interface for the mpv client. Since we did not have such a class I did not consider implementing the interesting suggestion from @CarterLi in PR #4367 of using mpv to display previews of JPEG XL screenshots. In macOS Sonoma AppKit renders the preview of JPEG XL screenshots, so going forward mpv is not needed for that. However we still at some point need support for HDR screenshots. One way to do that would be to move away from our proprietary thumbnail file format and store the thumbnails as a MP4 and use mpv to display them. A discussion for another day. Anyway, just an example of how a more isolated mpv client could have been used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I have addressed the comments. I added a comment about the main thread requirement. I moved the code that was calling back into mpv to PlayerCore with checks for shutting down.
iina/MPVController.swift
Outdated
DispatchQueue.main.async(execute: self.player.mainWindow.toggleWindowFullScreen) | ||
DispatchQueue.main.async { [self] in | ||
guard player.mainWindow.loaded else { return } | ||
let fs = getFlag(MPVOption.Window.fullscreen) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getFlag
should be moved out
iina/MPVController.swift
Outdated
self.player.mainWindow.setWindowFloatingOnTop(ontop) | ||
DispatchQueue.main.async { [self] in | ||
guard player.mainWindow.loaded else { return } | ||
let ontop = getFlag(MPVOption.Window.ontop) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
iina/MPVController.swift
Outdated
self.player.mainWindow.setWindowScale(windowScale) | ||
DispatchQueue.main.async { [self] in | ||
guard player.mainWindow.loaded else { return } | ||
let windowScale = getDouble(MPVOption.Window.windowScale) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
This commit will: - Change MPVController to perform most event processing using the main thread - Remove dispatching to main queue from PlayerCore methods now being called by MPVController using the main thread - Add the Atomic property wrapper to the playlistTotalLength and playlistTotalLengthIsReady PlaylistViewController properties - Add the Atomic property wrapper to the following PlayerInfo properties: - aid - secondSid - sid - subTracks - thumbnails - thumbnailsProgress - thumbnailsReady - vid - Change references to subTracks to lock the array - Change references to thumbnails to lock the array - Add the Atomic property wrapper to the hooks MPVController property - Change references to hooks to lock the array - Add the Atomic property wrapper to the playlistTotalLengthIsReady and playlistTotalLength PlaylistViewController properties - Change PlayerCore.playbackRestarted to re-enable the OSD by dispatching to the main thread using async with a delay instead of using a timer - Add the method logPropertyValueError to MPVController to report when property values can not be converted to the expected type - Replace MPVController method reEnableOSDAfterFileLoading with inline code These changes will correct data races between the main thread and the thread for the MPVController com.colliderli.iina.controller queue by changing MPVController to dispatch work to the main queue. This will simplify the code as in some cases work was being done on both the controller queue and the main queue as some processing required calling AppKit methods that must be run on the main thread. These changes will also correct the data races between the main thread and PlayerCore backgroundQueue used for loading subtitles by adding the Atomic property wrapper to properties shared between these threads and using the withLock method. Similar changes correct the data races between the main thread and PlayerCore thumbnailQueue. These changes will also fix data races with the playlistTotalLengthIsReady and playlistTotalLength properties.
This commit will: - Change MPVController to perform most event processing using the main thread - Remove dispatching to main queue from PlayerCore methods now being called by MPVController using the main thread - Add the Atomic property wrapper to the playlistTotalLength and playlistTotalLengthIsReady PlaylistViewController properties - Add the Atomic property wrapper to the following PlayerInfo properties: - aid - secondSid - sid - subTracks - thumbnails - thumbnailsProgress - thumbnailsReady - vid - Change references to subTracks to lock the array - Change references to thumbnails to lock the array - Add the Atomic property wrapper to the hooks MPVController property - Change references to hooks to lock the array - Add the Atomic property wrapper to the playlistTotalLengthIsReady and playlistTotalLength PlaylistViewController properties - Change PlayerCore.playbackRestarted to re-enable the OSD by dispatching to the main thread using async with a delay instead of using a timer - Add the method logPropertyValueError to MPVController to report when property values can not be converted to the expected type - Replace MPVController method reEnableOSDAfterFileLoading with inline code These changes will correct data races between the main thread and the thread for the MPVController com.colliderli.iina.controller queue by changing MPVController to dispatch work to the main queue. This will simplify the code as in some cases work was being done on both the controller queue and the main queue as some processing required calling AppKit methods that must be run on the main thread. These changes will also correct the data races between the main thread and PlayerCore backgroundQueue used for loading subtitles by adding the Atomic property wrapper to properties shared between these threads and using the withLock method. Similar changes correct the data races between the main thread and PlayerCore thumbnailQueue. These changes will also fix data races with the playlistTotalLengthIsReady and playlistTotalLength properties.
This commit will: - Change MPVController to perform most event processing using the main thread - Remove dispatching to main queue from PlayerCore methods now being called by MPVController using the main thread - Add the Atomic property wrapper to the playlistTotalLength and playlistTotalLengthIsReady PlaylistViewController properties - Add the Atomic property wrapper to the following PlayerInfo properties: - aid - secondSid - sid - subTracks - thumbnails - thumbnailsProgress - thumbnailsReady - vid - Change references to subTracks to lock the array - Change references to thumbnails to lock the array - Add the Atomic property wrapper to the hooks MPVController property - Change references to hooks to lock the array - Add the Atomic property wrapper to the playlistTotalLengthIsReady and playlistTotalLength PlaylistViewController properties - Change PlayerCore.playbackRestarted to re-enable the OSD by dispatching to the main thread using async with a delay instead of using a timer - Add the method logPropertyValueError to MPVController to report when property values can not be converted to the expected type - Replace MPVController method reEnableOSDAfterFileLoading with inline code These changes will correct data races between the main thread and the thread for the MPVController com.colliderli.iina.controller queue by changing MPVController to dispatch work to the main queue. This will simplify the code as in some cases work was being done on both the controller queue and the main queue as some processing required calling AppKit methods that must be run on the main thread. These changes will also correct the data races between the main thread and PlayerCore backgroundQueue used for loading subtitles by adding the Atomic property wrapper to properties shared between these threads and using the withLock method. Similar changes correct the data races between the main thread and PlayerCore thumbnailQueue. These changes will also fix data races with the playlistTotalLengthIsReady and playlistTotalLength properties.
This commit will:
MPVController
to perform most event processing using the main threadPlayerCore
methods now being called byMPVController
using the main threadAtomic
property wrapper to theplaylistTotalLength
andplaylistTotalLengthIsReady
PlaylistViewController
propertiesAtomic
property wrapper to the followingPlayerInfo
properties:subTracks
to lock the arraythumbnails
to lock the arrayAtomic
property wrapper to thehooks
MPVController
propertyhooks
to lock the arrayPlayerCore.playbackRestarted
to re-enable the OSD by dispatching to the main thread usingasync
with a delay instead of using a timerlogPropertyValueError
toMPVController
to report when property values can not be converted to the expected typeMPVController
methodreEnableOSDAfterFileLoading
with inline codeThese changes will correct data races between the main thread and the thread for the
MPVController
com.colliderli.iina.controller
queue by changingMPVController
to dispatch work to the main queue. This will simplify the code as in some cases work was being done on both thecontroller
queue and the main queue as some processing required calling AppKit methods that must be run on the main thread.These changes will correct the data races between the main thread and PlayerCore backgroundQueue used for loading subtitles by adding the
Atomic
property wrapper to properties shared between these threads and using thewithLock
method. Similar changes correct the data races between the main thread and PlayerCore thumbnailQueue.These changes will also fix data races with the
playlistTotalLengthIsReady
andplaylistTotalLength
properties.Description: