-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Create SafeBoosterHandle and SafeDataSetHandle #4539
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
Conversation
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.
Codecov Report
@@ Coverage Diff @@
## master #4539 +/- ##
==========================================
+ Coverage 75.12% 75.14% +0.02%
==========================================
Files 908 908
Lines 160214 160223 +9
Branches 17250 17250
==========================================
+ Hits 120355 120395 +40
+ Misses 35045 35009 -36
- Partials 4814 4819 +5
|
fc29ed4
to
5835578
Compare
public int BestIteration { get; set; } | ||
|
||
public Booster(Dictionary<string, object> parameters, Dataset trainset, Dataset validset = null) | ||
{ | ||
var param = LightGbmInterfaceUtils.JoinParameters(parameters); | ||
var handle = IntPtr.Zero; | ||
LightGbmInterfaceUtils.Check(WrappedLightGbmInterface.BoosterCreate(trainset.Handle, param, ref handle)); | ||
LightGbmInterfaceUtils.Check(WrappedLightGbmInterface.BoosterCreate(trainset.Handle, param, out var handle)); |
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.
📝 The change to SafeHandle means that the finalizer for SafeBoosterHandle
will now be invoked at some point if the call to BoosterCreate
succeeds but an exception is thrown later in this constructor (i.e. an exception prevents the caller from being able to use Booster.Dispose()
). Ideally, code which constructs disposable objects should be robust in ensuring created objects are disposed if a failure would prevent those objects from being disposed by the caller. I'm not ready to submit the change to ensure all the callers dispose of objects deterministically on exceptional paths, but I'd like to merge this one in the meantime.
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.
Doing a quick scan of the usages of the Booster and Dataset classes, they are always disposed of in the product code (one in a using
and the other in a try-finally
. The only case I saw where they aren't disposed of is in the tests.
var gbmNative = WrappedLightGbmTraining.Train(ch, pch, gbmParams, gbmDataSet, numIteration: numberOfTrainingIterations); |
var gbmDataSet = new Trainers.LightGbm.Dataset(sampleValueGroupedByColumn, sampleIndicesGroupedByColumn, _columnNumber, sampleNonZeroCntPerColumn, _rowNumber, _rowNumber, "", floatLabels); |
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.
The Booster
class is disposed if the constructor returns without throwing an exception. However, if LightGbmInterfaceUtils.Check
throws an exception from the constructor itself, the SafeBoosterHandle
will have been created by the call to BoosterCreate
but never stored in an object that is later disposed. Prior to the use of a SafeHandle
, an instance in this scenario would never be cleaned up. Now it will be cleaned up by the finalizer.
5835578
to
79c7cfa
Compare
These safe handles were created based on the crash data in https://dev.azure.com/dnceng/public/_build/results?buildId=449745&view=logs.