Open cilki opened 5 years ago
As a starting idea, since this issue arose from a conversation between @cilki and I, I'd propose that we need to implement another part of the API to handle this separate from the elements that allow access to the values.
My first idea is to have configuration variables for the drivers, that allow us to set them between stateless (where each request for a value would trigger a full fetch/parse/return cycle) and statefull (where some kind of session and state would be handled from within the driver itself).
Once we have a way to set only specific Drivers into a statefull flow, we need some accessors in the API to handle those states.
These accessors would be able to be started and stopped as needed, and could also implement AutoClosable
for ease of use.
This could be some general purpose accessor that we can call with something like:
//Set the driver to statefull in the config
try (new DriverSession("os.windows.wmic"))
{
//Code using OSHI in here
}
just as well as
//Set the driver as statefull in the config
OSHI oshi = new OSHI();
//Code using OSHI
oshi.DriverSessionsHandler().closeSession("os.windows.wmic");
//This could also be done with a general closeAllSessions() call
The idea would be that, if a Driver is set as statefull, it would not close the session unless we explicitly told it to, through the Handler, or by using AutoClosable
. As a counter, any time we require something from the Driver, and there's no session, a new one is created.
The only problem is, how can you determine when to release a driver's resources without a cue from a higher layer?
I'm not sure we need to answer this globally. There are very few resources that we need to keep open, and we should minimize those. Command lines are just a process that executes and ends. We don't want an interactive command line. Reading data from files (/proc
etc.) should close the file when done.
Every resource I've tried to hold on to has caused a later bug. :)
With WMI, I tried holding onto connections, or even the COM initialization, starting in 3.7. As I've recently seen, that was a mistake. COM should be initialized and uninitialized with each query. To leave it open may prevent other code from executing.
My PDH counter implementation had a handle leak that's since been fixed. But thinking about all the data access objects, PDH counter handles are the only one that I think needs to stay open (and the PDH object does have methods to remove those counters).
Building on the discussion of state, other than the (serializable) data objects which will hold the latest information queried until the designated update/refresh time, don't hold on to data either. Make that a responsibility of the user. Take CPU usage, for example. We track the previous set of ticks (that we returned to the user). Why? To enable a "CPU Usage between ticks" method (which was a backup if the user didn't have the Oracle JVM's method)... but that's only relevant since the last time the user called the method. In hindsight we shouldn't have it; or if we have it it needs to take an argument -- the previous tick array! So a user could give it the ticks from 1, 5, or 15 seconds/minutes ago to get whichever average they wanted. (And yes, it's just a convenience util class to do math the user could do on their own.) OSHI's core API should only care about the current set of ticks, nothing else.
Same with USB Devices, or Processes. Why do we cache them internally? Just let the user provide us a list (the previous list, perhaps) if they want the next result to run faster.
If we want to build a state layer, build it as an optional feature on top of the core API. Have an object that tracks (or polls!) CPU ticks over time that the User can query to get CPU averages on-demand. Have an object that tracks (or polls) the processes and can alert the user to what's started/stopped. What USB devices are plugged in/removed.
Back to the original question: the only resource I think we need to hold on to is PDH counter handles, so perhaps that use case can guide the discussion.
@dbwiddis That's more or less the reasoning around my separation between drivers, caching, and API; and why I wanted to have caching as an optional/configurable feature.
The only problem is, how can you determine when to release a driver's resources without a cue from a higher layer? Making the lowest layer stateful introduces a session-like architecture to the higher layers.
Originally posted by @cilki in https://github.com/oshi/oshi5/issues/7#issuecomment-452405083