Update ^C Handling during upload|download operations

* Update ^C Handling during upload|download operations
This commit is contained in:
abraunegg 2024-04-28 09:05:30 +10:00
parent 39c04642cd
commit bf029b4676
5 changed files with 46 additions and 67 deletions

View file

@ -161,13 +161,14 @@ class CurlResponse {
class CurlEngine { class CurlEngine {
__gshared CurlEngine[] curlEnginePool; // Shared pool of CurlEngine instances accessible across all threads
__gshared CurlEngine[] curlEnginePool; // __gshared is used to declare a variable that is shared across all threads
HTTP http; HTTP http;
File uploadFile;
CurlResponse response;
bool keepAlive; bool keepAlive;
ulong dnsTimeout; ulong dnsTimeout;
CurlResponse response;
File uploadFile;
string internalThreadId; string internalThreadId;
this() { this() {
@ -176,31 +177,27 @@ class CurlEngine {
internalThreadId = generateAlphanumericString(); internalThreadId = generateAlphanumericString();
} }
// The destructor should only clean up resources owned directly by this instance
~this() { ~this() {
// The destructor should only clean up resources owned directly by this instance // Is the file still open?
addLogEntry("CurlEngine DESTRUCTOR CALLED", ["debug"]);
addLogEntry("CurlEngine DESTRUCTOR CALLED");
// Avoid modifying or destroying shared/static resources here
if (uploadFile.isOpen()) { if (uploadFile.isOpen()) {
uploadFile.close(); uploadFile.close();
} }
// Cleanup class memory usage // Is 'response' cleared?
object.destroy(uploadFile); // Destroy, however we cant set to null if (response !is null) {
object.destroy(response); // Destroy, then set to null object.destroy(response); // Destroy, then set to null
response = null; response = null;
internalThreadId = null; }
// Is the actual http instance is stopped? // Is the actual http instance is stopped?
if (!http.isStopped) { if (!http.isStopped) {
// instance was not stopped .. need to stop it // HTTP instance was not stopped .. need to stop it
addLogEntry("CurlEngine DESTRUCTOR HTTP INSTANCE WAS NOT STOPPED - STOPPING", ["debug"]);
http.shutdown(); http.shutdown();
object.destroy(http); // Destroy, however we cant set to null
} }
object.destroy(http); // Destroy, however we cant set to null
} }
static CurlEngine getCurlInstance() { static CurlEngine getCurlInstance() {
synchronized (CurlEngine.classinfo) { synchronized (CurlEngine.classinfo) {
// What is the current pool size // What is the current pool size
@ -232,6 +229,7 @@ class CurlEngine {
synchronized (CurlEngine.classinfo) { synchronized (CurlEngine.classinfo) {
// What is the current pool size // What is the current pool size
addLogEntry("CURL ENGINES TO RELEASE: " ~ to!string(curlEnginePool.length), ["debug"]); addLogEntry("CURL ENGINES TO RELEASE: " ~ to!string(curlEnginePool.length), ["debug"]);
addLogEntry("CURL ENGINES TO RELEASE: " ~ to!string(curlEnginePool.length));
// Safely iterate and clean up each CurlEngine instance // Safely iterate and clean up each CurlEngine instance
foreach (curlEngineInstance; curlEnginePool) { foreach (curlEngineInstance; curlEnginePool) {
@ -253,12 +251,11 @@ class CurlEngine {
// Destroy all curl instances // Destroy all curl instances
static void destroyAllCurlInstances() { static void destroyAllCurlInstances() {
addLogEntry("DESTROY ALL CURL ENGINES", ["debug"]); addLogEntry("DESTROY ALL CURL ENGINES", ["debug"]);
addLogEntry("DESTROY ALL CURL ENGINES");
// Release all 'curl' instances // Release all 'curl' instances
releaseAllCurlInstances(); releaseAllCurlInstances();
// Destroy curlEnginePool, set to null
object.destroy(curlEnginePool);
curlEnginePool = null;
} }
// We are releasing a curl instance back to the pool // We are releasing a curl instance back to the pool

View file

@ -53,6 +53,7 @@ class LogBuffer {
flushThread.start(); flushThread.start();
} }
// The destructor should only clean up resources owned directly by this instance
~this() { ~this() {
object.destroy(bufferLock); object.destroy(bufferLock);
object.destroy(condReady); object.destroy(condReady);

View file

@ -1367,13 +1367,8 @@ extern(C) nothrow @nogc @system void exitHandler(int value) {
assumeNoGC ( () { assumeNoGC ( () {
addLogEntry("\nReceived termination signal, initiating cleanup"); addLogEntry("\nReceived termination signal, initiating cleanup");
// Wait for all parallel jobs that depend on the database to complete // Wait for all parallel jobs that depend on the database to complete
addLogEntry("Waiting for any existing upload|download process to complete"); addLogEntry("Waiting for any existing upload|download process to complete");
taskPool.finish(true); syncEngineInstance.shutdown();
// Force kill any running threads
//addLogEntry("Forcing any active thread to exit");
//taskPool.finish(false);
// Perform the shutdown process // Perform the shutdown process
performSynchronisedExitProcess("exitHandler"); performSynchronisedExitProcess("exitHandler");
@ -1403,17 +1398,16 @@ void performSynchronisedExitProcess(string scopeCaller = null) {
shutdownOneDriveWebhook(); shutdownOneDriveWebhook();
// Shutdown the client side filtering objects // Shutdown the client side filtering objects
shutdownSelectiveSync(); shutdownSelectiveSync();
// Destroy all 'curl' instances
destroyCurlInstances();
// Shutdown the sync engine // Shutdown the sync engine
shutdownSyncEngine(); shutdownSyncEngine();
// Shutdown any local filesystem monitoring // Shutdown any local filesystem monitoring
shutdownFilesystemMonitor(); shutdownFilesystemMonitor();
// Shutdown the database // Shutdown the database
shutdownDatabase(); shutdownDatabase();
// Destroy all 'curl' instances
destroyCurlInstances();
// Shutdown the application configuration objects // Shutdown the application configuration objects
shutdownAppConfig(); shutdownAppConfig();
} catch (Exception e) { } catch (Exception e) {
addLogEntry("Error during performStandardExitProcess: " ~ e.toString(), ["error"]); addLogEntry("Error during performStandardExitProcess: " ~ e.toString(), ["error"]);
} }
@ -1449,7 +1443,7 @@ void shutdownFilesystemMonitor() {
void shutdownSelectiveSync() { void shutdownSelectiveSync() {
if (selectiveSync !is null) { if (selectiveSync !is null) {
addLogEntry("Shutdown Client Side Filtering instance", ["debug"]); addLogEntry("Shutdown Client Side Filtering instance", ["debug"]);
selectiveSync.shutdown(); selectiveSync.shutdown();
object.destroy(selectiveSync); object.destroy(selectiveSync);
selectiveSync = null; selectiveSync = null;
} }
@ -1458,7 +1452,7 @@ void shutdownSelectiveSync() {
void shutdownSyncEngine() { void shutdownSyncEngine() {
if (syncEngineInstance !is null) { if (syncEngineInstance !is null) {
addLogEntry("Shutdown Sync Engine instance", ["debug"]); addLogEntry("Shutdown Sync Engine instance", ["debug"]);
//syncEngineInstance.shutdown(); - potentially need this and also check for a ~this() for class cleanup syncEngineInstance.shutdown(); // Make sure any running thread completes first
object.destroy(syncEngineInstance); object.destroy(syncEngineInstance);
syncEngineInstance = null; syncEngineInstance = null;
} }
@ -1480,7 +1474,7 @@ void shutdownAppConfig() {
// We were running with --dry-run , clean up the applicable database // We were running with --dry-run , clean up the applicable database
cleanupDryRunDatabaseFiles(runtimeDatabaseFile); cleanupDryRunDatabaseFiles(runtimeDatabaseFile);
} }
object.destroy(appConfig); object.destroy(appConfig);
appConfig = null; appConfig = null;
} }
} }

View file

@ -111,17 +111,19 @@ class OneDriveApi {
siteSearchUrl = appConfig.globalGraphEndpoint ~ "/v1.0/sites?search"; siteSearchUrl = appConfig.globalGraphEndpoint ~ "/v1.0/sites?search";
siteDriveUrl = appConfig.globalGraphEndpoint ~ "/v1.0/sites/"; siteDriveUrl = appConfig.globalGraphEndpoint ~ "/v1.0/sites/";
// Subscriptions // Subscriptions
subscriptionUrl = appConfig.globalGraphEndpoint ~ "/v1.0/subscriptions"; subscriptionUrl = appConfig.globalGraphEndpoint ~ "/v1.0/subscriptions";
} }
// The destructor should only clean up resources owned directly by this instance
~this() { ~this() {
// We cant destroy 'appConfig' here as this leads to a segfault if (curlEngine !is null) {
object.destroy(curlEngine); curlEngine = null;
object.destroy(response); }
curlEngine = null;
response = null; if (response !is null) {
response = null;
}
} }
// Initialise the OneDrive API class // Initialise the OneDrive API class
@ -353,12 +355,6 @@ class OneDriveApi {
return authorised; return authorised;
} }
// Reinitialise the OneDrive API class
bool reinitialise() {
releaseCurlEngine();
return initialise(this.keepAlive);
}
// If the API has been configured correctly, print the items that been configured // If the API has been configured correctly, print the items that been configured
void debugOutputConfiguredAPIItems() { void debugOutputConfiguredAPIItems() {
// Debug output of configured URL's // Debug output of configured URL's
@ -1120,7 +1116,6 @@ class OneDriveApi {
// Wrapper function for all requests to OneDrive API // Wrapper function for all requests to OneDrive API
// - This should throw a OneDriveException so that this exception can be handled appropriately elsewhere in the application // - This should throw a OneDriveException so that this exception can be handled appropriately elsewhere in the application
private JSONValue oneDriveErrorHandlerWrapper(CurlResponse delegate(CurlResponse response) executer, bool validateJSONResponse, string callingFunction, int lineno) { private JSONValue oneDriveErrorHandlerWrapper(CurlResponse delegate(CurlResponse response) executer, bool validateJSONResponse, string callingFunction, int lineno) {
// Create a new 'curl' response // Create a new 'curl' response
response = new CurlResponse(); response = new CurlResponse();

View file

@ -185,9 +185,9 @@ class SyncEngine {
this(ApplicationConfig appConfig, ItemDatabase itemDB, ClientSideFiltering selectiveSync) { this(ApplicationConfig appConfig, ItemDatabase itemDB, ClientSideFiltering selectiveSync) {
// Create the specific task pool to process items in parallel // Create the specific task pool to process items in parallel
this.processPool = new TaskPool(to!int(appConfig.getValueLong("threads"))); processPool = new TaskPool(to!int(appConfig.getValueLong("threads")));
addLogEntry("PROCESS POOL WORKER THREADS: " ~ to!string(processPool.size), ["debug"]); addLogEntry("PROCESS POOL WORKER THREADS: " ~ to!string(processPool.size), ["debug"]);
// Configure the class varaible to consume the application configuration // Configure the class varaible to consume the application configuration
this.appConfig = appConfig; this.appConfig = appConfig;
// Configure the class varaible to consume the database configuration // Configure the class varaible to consume the database configuration
@ -302,23 +302,15 @@ class SyncEngine {
} }
} }
~this() { // The destructor should only clean up resources owned directly by this instance
this.processPool.finish(true); ~this() {
object.destroy(this.processPool); // Destroy, then set to null processPool.finish(true);
this.processPool = null;
object.destroy(this.appConfig); // Destroy, then set to null
this.appConfig = null;
object.destroy(this.itemDB); // Destroy, then set to null
this.itemDB = null;
object.destroy(this.selectiveSync); // Destroy, then set to null
this.selectiveSync = null;
} }
// Initialise the Sync Engine class // Initialise the Sync Engine class
bool initialise() { bool initialise() {
// Control whether the worker threads are daemon threads. A daemon thread is automatically terminated when all non-daemon threads have terminated. // Control whether the worker threads are daemon threads. A daemon thread is automatically terminated when all non-daemon threads have terminated.
processPool.isDaemon(true); processPool.isDaemon(true);
// Create a new instance of the OneDrive API // Create a new instance of the OneDrive API
OneDriveApi oneDriveApiInstance; OneDriveApi oneDriveApiInstance;
@ -387,15 +379,15 @@ class SyncEngine {
// Shutdown this API instance, as we will create API instances as required, when required // Shutdown this API instance, as we will create API instances as required, when required
oneDriveApiInstance.releaseCurlEngine(); oneDriveApiInstance.releaseCurlEngine();
// Free object and memory
//object.destroy(oneDriveApiInstance);
//oneDriveApiInstance = null;
return true; return true;
} }
// Shutdown the sync engine, wait for anything in processPool to complete
void shutdown() {
addLogEntry("SYNC-ENGINE: Waiting for all internal threads to complete", ["debug"]);
processPool.finish(true);
}
// Get Default Drive Details for this Account // Get Default Drive Details for this Account
void getDefaultDriveDetails() { void getDefaultDriveDetails() {