HaikuDepot: Fix Crash on Shutdown

The MainWindow itself was a BReferencable via a listener
virtual class.  It looks like the error has come from a
conflict between the MainWindow's own deletion and the
state of the reference count.  This change moves the
listener / BReferencable out of the MainWindow so that
the lifecycle of the BReferenable can be managed
correctly.

A further problem here is that the new listener
was leaked from the MainWindow class on shutdown.
To resolve this problem requires a considerable
change to the "process coordinator" system.

Fixes #17689

Change-Id: I7230843ba05537015f4a597b4a616b96c6db3dde
Reviewed-on: https://review.haiku-os.org/c/haiku/+/5285
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Reviewed-by: Andrew Lindesay <apl@lindesay.co.nz>
This commit is contained in:
Andrew Lindesay 2022-05-01 21:48:28 +12:00
parent 26f691760c
commit 409af93462
21 changed files with 387 additions and 115 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LOGGER_H
@ -14,6 +14,9 @@
#include <stdlib.h>
#define MILLIS_IN_DAY (1000 * 60 * 60 * 24)
// These macros allow for standardized logging to be output.
// The use of macros in this way means that the use of the log is concise where
// it is used and also because the macro unwraps to a block contained with a
@ -23,7 +26,11 @@
// conditional clauses in the code to prevent this otherwise would be
// cumbersome.
#define HDLOGPREFIX(L) printf("{%c} ", toupper(Logger::NameForLevel(L)[0]));
#define HDLOGPREFIX(L) printf("@%08" B_PRId64 " {%c} <t:%" B_PRId32 "> ", \
((system_time() / 1000) % MILLIS_IN_DAY), \
toupper(Logger::NameForLevel(L)[0]), \
abs(find_thread(NULL) % 1000) \
);
#define HDLOG(L, M...) do { if (Logger::IsLevelEnabled(L)) { \
HDLOGPREFIX(L) \

View File

@ -519,6 +519,8 @@ Model::CanPopulatePackage(const PackageInfoRef& package)
void
Model::PopulatePackage(const PackageInfoRef& package, uint32 flags)
{
HDTRACE("will populate package for [%s]", package->Name().String());
if (!CanPopulatePackage(package)) {
HDINFO("unable to populate package [%s]", package->Name().String());
return;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include "AbstractProcess.h"
@ -14,6 +14,7 @@
#include "HaikuDepotConstants.h"
#include "Logger.h"
#include "ProcessListener.h"
AbstractProcess::AbstractProcess()
@ -33,16 +34,20 @@ AbstractProcess::~AbstractProcess()
void
AbstractProcess::SetListener(AbstractProcessListener* listener)
AbstractProcess::SetListener(ProcessListener* listener)
{
AutoLocker<BLocker> locker(&fLock);
fListener = BReference<AbstractProcessListener>(listener);
if (fListener != listener) {
AutoLocker<BLocker> locker(&fLock);
fListener = listener;
}
}
status_t
AbstractProcess::Run()
{
ProcessListener* listener;
{
AutoLocker<BLocker> locker(&fLock);
@ -57,26 +62,27 @@ AbstractProcess::Run()
}
fProcessState = PROCESS_RUNNING;
listener = fListener;
}
if (listener != NULL)
listener->ProcessChanged();
status_t runResult = RunInternal();
if (runResult != B_OK)
HDERROR("[%s] an error has arisen; %s", Name(), strerror(runResult));
BReference<AbstractProcessListener> listener;
{
AutoLocker<BLocker> locker(&fLock);
fProcessState = PROCESS_COMPLETE;
fErrorStatus = runResult;
listener = fListener;
}
// this process may be part of a larger bulk-load process and
// if so, the process orchestration needs to know when this
// process has completed.
if (listener.IsSet())
if (listener != NULL)
listener->ProcessChanged();
return runResult;
@ -110,7 +116,7 @@ status_t
AbstractProcess::Stop()
{
status_t result = B_CANCELED;
BReference<AbstractProcessListener> listener = NULL;
ProcessListener* listener = NULL;
{
AutoLocker<BLocker> locker(&fLock);
@ -126,7 +132,7 @@ AbstractProcess::Stop()
}
}
if (listener.IsSet())
if (listener != NULL)
listener->ProcessChanged();
return result;
@ -165,11 +171,11 @@ AbstractProcess::Progress()
void
AbstractProcess::_NotifyChanged()
{
BReference<AbstractProcessListener> listener = NULL;
ProcessListener* listener = NULL;
{
AutoLocker<BLocker> locker(&fLock);
listener = fListener;
}
if (listener.IsSet())
if (listener != NULL)
listener->ProcessChanged();
}

View File

@ -1,12 +1,11 @@
/*
* Copyright 2018-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_PROCESS_H
#define ABSTRACT_PROCESS_H
#include <String.h>
#include <Referenceable.h>
#include <Url.h>
#include "StandardMetaData.h"
@ -20,15 +19,7 @@ typedef enum process_state {
} process_state;
/*! Clients are able to subclass from this 'interface' in order to accept
call-backs when a process has exited; either through success or through
failure.
*/
class AbstractProcessListener : public BReferenceable {
public:
virtual void ProcessChanged() = 0;
};
class ProcessListener;
/*! This is the superclass of all Processes. */
@ -47,7 +38,8 @@ public:
bool IsRunning();
bool WasStopped();
process_state ProcessState();
void SetListener(AbstractProcessListener* listener);
void SetListener(ProcessListener* listener);
protected:
virtual status_t RunInternal() = 0;
@ -58,8 +50,7 @@ protected:
BLocker fLock;
private:
BReference<AbstractProcessListener>
fListener;
ProcessListener* fListener;
bool fWasStopped;
process_state fProcessState;
status_t fErrorStatus;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2021-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -21,6 +21,8 @@
AbstractProcessNode::AbstractProcessNode(AbstractProcess* process)
:
fLock(),
fListener(NULL),
fProcess(process)
{
}
@ -39,6 +41,23 @@ AbstractProcessNode::Process() const
}
bool
AbstractProcessNode::IsRunning()
{
return Process()->ProcessState() != PROCESS_COMPLETE;
}
void
AbstractProcessNode::SetListener(ProcessListener* listener)
{
if (fListener != listener) {
AutoLocker<BLocker> locker(&fLock);
fListener = listener;
}
}
/*! This method will spin-lock the thread until the process is in one of the
states defined by the mask.
*/

View File

@ -1,16 +1,19 @@
/*
* Copyright 2018-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef ABSTRACT_PROCESS_NODE_H
#define ABSTRACT_PROCESS_NODE_H
#include <AutoLocker.h>
#include <Locker.h>
#include <ObjectList.h>
#include <OS.h>
class AbstractProcess;
class ProcessListener;
/*! This class is designed to be used by the ProcessCoordinator class. The
@ -26,6 +29,7 @@ public:
AbstractProcess* Process() const;
virtual status_t Start() = 0;
virtual status_t RequestStop() = 0;
virtual bool IsRunning();
void AddPredecessor(AbstractProcessNode* node);
int32 CountPredecessors() const;
@ -37,11 +41,17 @@ public:
AbstractProcessNode*
SuccessorAt(int32 index) const;
virtual void SetListener(ProcessListener* listener);
protected:
status_t _SpinUntilProcessState(
uint32 desiredStatesMask,
int32 timeoutSeconds);
protected:
BLocker fLock;
ProcessListener* fListener;
private:
void _AddSuccessor(AbstractProcessNode* node);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -9,6 +9,7 @@
#include <AutoLocker.h>
#include <Catalog.h>
#include <StringFormat.h>
#include <Uuid.h>
#include "Logger.h"
@ -16,15 +17,54 @@
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ProcessCoordinator"
#define LOCK_TIMEOUT_MICROS (1000 * 1000)
// These are keys that are used to store the ProcessCoordinatorState data into
// a BMessage instance.
#define KEY_PROCESS_COORDINATOR_IDENTIFIER "processCoordinatorIdentifier"
#define KEY_PROGRESS "progress"
#define KEY_MESSAGE "message"
#define KEY_IS_RUNNING "isRunning"
#define KEY_ERROR_STATUS "errorStatus"
// #pragma mark - ProcessCoordinatorState implementation
ProcessCoordinatorState::ProcessCoordinatorState(BMessage* from)
{
if (from->FindString(KEY_PROCESS_COORDINATOR_IDENTIFIER,
&fProcessCoordinatorIdentifier) != B_OK) {
HDFATAL("unable to find the key [%s]",
KEY_PROCESS_COORDINATOR_IDENTIFIER);
}
if (from->FindFloat(KEY_PROGRESS, &fProgress) != B_OK) {
HDFATAL("unable to find the key [%s]", KEY_PROGRESS);
}
if (from->FindString(KEY_MESSAGE, &fMessage) != B_OK) {
HDFATAL("unable to find the key [%s]", KEY_MESSAGE);
}
if (from->FindBool(KEY_IS_RUNNING, &fIsRunning) != B_OK) {
HDFATAL("unable to find the key [%s]", KEY_IS_RUNNING);
}
int64 errorStatusNumeric;
if (from->FindInt64(KEY_ERROR_STATUS, &errorStatusNumeric) != B_OK) {
HDFATAL("unable to find the key [%s]", KEY_ERROR_STATUS);
}
fErrorStatus = static_cast<status_t>(errorStatusNumeric);
}
ProcessCoordinatorState::ProcessCoordinatorState(
const ProcessCoordinator* processCoordinator, float progress,
const BString& message, bool isRunning, status_t errorStatus)
:
fProcessCoordinator(processCoordinator),
fProcessCoordinatorIdentifier(processCoordinator->Identifier()),
fProgress(progress),
fMessage(message),
fIsRunning(isRunning),
@ -38,10 +78,10 @@ ProcessCoordinatorState::~ProcessCoordinatorState()
}
const ProcessCoordinator*
ProcessCoordinatorState::Coordinator() const
const BString
ProcessCoordinatorState::ProcessCoordinatorIdentifier() const
{
return fProcessCoordinator;
return fProcessCoordinatorIdentifier;
}
@ -73,15 +113,39 @@ ProcessCoordinatorState::ErrorStatus() const
}
status_t
ProcessCoordinatorState::Archive(BMessage* into, bool deep) const
{
status_t result = B_OK;
if (result == B_OK) {
result = into->AddString(KEY_PROCESS_COORDINATOR_IDENTIFIER,
fProcessCoordinatorIdentifier);
}
if (result == B_OK)
result = into->AddFloat(KEY_PROGRESS, fProgress);
if (result == B_OK)
result = into->AddString(KEY_MESSAGE, fMessage);
if (result == B_OK)
result = into->AddBool(KEY_IS_RUNNING, fIsRunning);
if (result == B_OK)
result = into->AddInt64(KEY_ERROR_STATUS, static_cast<int64>(fErrorStatus));
return result;
}
// #pragma mark - ProcessCoordinator implementation
ProcessCoordinator::ProcessCoordinator(const char* name, BMessage* message)
:
fName(name),
fLock(),
fCoordinateAndCallListenerRerun(false),
fCoordinateAndCallListenerRerunLock(),
fListener(NULL),
fMessage(message),
fWasStopped(false)
fWasStopped(false),
fIdentifier(BUuid().ToString())
{
}
@ -97,9 +161,15 @@ ProcessCoordinator::~ProcessCoordinator()
delete fMessage;
}
const BString&
ProcessCoordinator::Identifier() const
{
return fIdentifier;
}
void
ProcessCoordinator::SetListener(ProcessCoordinatorListener *listener)
ProcessCoordinator::SetListener(ProcessCoordinatorListener* listener)
{
fListener = listener;
}
@ -110,6 +180,7 @@ ProcessCoordinator::AddNode(AbstractProcessNode* node)
{
AutoLocker<BLocker> locker(&fLock);
fNodes.AddItem(node);
node->SetListener(this);
node->Process()->SetListener(this);
}
@ -126,7 +197,8 @@ ProcessCoordinator::IsRunning()
{
AutoLocker<BLocker> locker(&fLock);
for (int32 i = 0; i < fNodes.CountItems(); i++) {
if (_IsRunning(fNodes.ItemAt(i)))
AbstractProcessNode* node = fNodes.ItemAt(i);
if (node->IsRunning())
return true;
}
@ -237,13 +309,15 @@ ProcessCoordinator::_CreateStatusMessage()
for (int32 i = fNodes.CountItems() - 1; i >= 0; i--) {
AbstractProcess* process = fNodes.ItemAt(i)->Process();
if (process->ProcessState() == PROCESS_RUNNING) {
if (firstProcessDescription.IsEmpty()) {
firstProcessDescription = process->Description();
} else {
additionalRunningProcesses++;
if (strlen(process->Description()) != 0)
firstProcessDescription = process->Description();
else
additionalRunningProcesses++;
}
else
additionalRunningProcesses++;
}
}
@ -274,13 +348,40 @@ ProcessCoordinator::_CreateStatus()
}
/*! This will try to obtain the lock and if it cannot obtain the lock then
it will flag that when the coordinator has finished its current
coordination, it should initiate another coordination.
*/
void
ProcessCoordinator::_CoordinateAndCallListener()
{
if (fLock.LockWithTimeout(LOCK_TIMEOUT_MICROS) != B_OK) {
HDDEBUG("[Coordinator] would coordinate nodes, but coordination is "
"in progress - will defer");
AutoLocker<BLocker> locker(&fCoordinateAndCallListenerRerunLock);
fCoordinateAndCallListenerRerun = true;
return;
}
ProcessCoordinatorState state = _Coordinate();
if (fListener != NULL)
fListener->CoordinatorChanged(state);
fLock.Unlock();
bool coordinateAndCallListenerRerun = false;
{
AutoLocker<BLocker> locker(&fCoordinateAndCallListenerRerunLock);
coordinateAndCallListenerRerun = fCoordinateAndCallListenerRerun;
fCoordinateAndCallListenerRerun = false;
}
if (coordinateAndCallListenerRerun) {
HDDEBUG("[Coordinator] will run deferred coordination");
_CoordinateAndCallListener();
}
}
@ -347,13 +448,6 @@ ProcessCoordinator::_StopSuccessorNodes(AbstractProcessNode* predecessorNode)
}
bool
ProcessCoordinator::_IsRunning(AbstractProcessNode* node)
{
return node->Process()->ProcessState() != PROCESS_COMPLETE;
}
int32
ProcessCoordinator::_CountNodesCompleted()
{

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROCESS_COORDINATOR_H
@ -11,6 +11,7 @@
#include "AbstractProcess.h"
#include "AbstractProcessNode.h"
#include "ProcessListener.h"
class ProcessCoordinator;
@ -20,8 +21,9 @@ class ProcessCoordinator;
it can be dealt with atomically without having call back to the coordinator.
*/
class ProcessCoordinatorState {
class ProcessCoordinatorState : public BArchivable {
public:
ProcessCoordinatorState(BMessage* from);
ProcessCoordinatorState(
const ProcessCoordinator*
processCoordinator,
@ -29,14 +31,15 @@ public:
bool isRunning, status_t errorStatus);
virtual ~ProcessCoordinatorState();
const ProcessCoordinator* Coordinator() const;
const BString ProcessCoordinatorIdentifier() const;
float Progress() const;
BString Message() const;
bool IsRunning() const;
status_t ErrorStatus() const;
status_t Archive(BMessage* into, bool deep = true) const;
private:
const ProcessCoordinator* fProcessCoordinator;
BString fProcessCoordinatorIdentifier;
float fProgress;
BString fMessage;
bool fIsRunning;
@ -80,20 +83,22 @@ public:
list of ProcessNode-s so that they are all completed in the correct order.
*/
class ProcessCoordinator : public AbstractProcessListener {
class ProcessCoordinator : public ProcessListener {
public:
ProcessCoordinator(
const char* name,
BMessage* message = NULL);
virtual ~ProcessCoordinator();
const BString& Identifier() const;
void SetListener(
ProcessCoordinatorListener *listener);
void AddNode(AbstractProcessNode* nodes);
void ProcessChanged();
// AbstractProcessListener
// ProcessListener
bool IsRunning();
@ -108,7 +113,6 @@ public:
BMessage* Message() const;
private:
bool _IsRunning(AbstractProcessNode* node);
void _CoordinateAndCallListener();
ProcessCoordinatorState
_Coordinate();
@ -122,12 +126,15 @@ private:
private:
BString fName;
BLocker fLock;
bool fCoordinateAndCallListenerRerun;
BLocker fCoordinateAndCallListenerRerunLock;
BObjectList<AbstractProcessNode>
fNodes;
ProcessCoordinatorListener*
fListener;
BMessage* fMessage;
bool fWasStopped;
BString fIdentifier;
};

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -68,7 +68,7 @@ ProcessCoordinatorFactory::CreateUserDetailVerifierCoordinator(
/* static */ ProcessCoordinator*
ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
PackageInfoListener *packageInfoListener,
PackageInfoListenerRef packageInfoListener,
Model* model, bool forceLocalUpdate)
{
bool areWorkingFilesAvailable = StorageUtils::AreWorkingFilesAvailable();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2018-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROCESS_COORDINATOR_FACTORY_H
@ -25,7 +25,7 @@ public:
const PackageInfoRef package);
static ProcessCoordinator* CreateBulkLoadCoordinator(
PackageInfoListener *packageInfoListener,
PackageInfoListenerRef packageInfoListener,
Model* model, bool forceLocalUpdate);
static ProcessCoordinator* CreateUserDetailVerifierCoordinator(

View File

@ -0,0 +1,20 @@
/*
* Copyright 2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef PROCESS_LISTENER_H
#define PROCESS_LISTENER_H
#include <Referenceable.h>
/*! Clients are able to subclass from this 'interface' in order to accept
call-backs when a process has exited; either through success or through
failure.
*/
class ProcessListener {
public:
virtual void ProcessChanged() = 0;
};
#endif // PROCESS_LISTENER_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2021-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -10,6 +10,7 @@
#include "AbstractProcess.h"
#include "Logger.h"
#include "ProcessListener.h"
#define TIMEOUT_UNTIL_STARTED_SECS_DEFAULT 10
@ -37,6 +38,28 @@ ThreadedProcessNode::ThreadedProcessNode(AbstractProcess* process)
ThreadedProcessNode::~ThreadedProcessNode()
{
if (IsRunning()) {
HDFATAL("the process node is being deleted while the thread is"
"still running");
}
}
bool
ThreadedProcessNode::IsRunning()
{
if (!AbstractProcessNode::IsRunning()) {
AutoLocker<BLocker> locker(fLock);
if (fWorker == B_BAD_THREAD_ID)
return false;
thread_info ti;
status_t status = get_thread_info(fWorker, &ti);
if (status != B_OK)
// implies that the thread has stopped
return false;
HDTRACE("[Node<%s>] thread still running...", Process()->Name());
}
return true;
}
@ -46,13 +69,14 @@ ThreadedProcessNode::~ThreadedProcessNode()
status_t
ThreadedProcessNode::Start()
{
AutoLocker<BLocker> locker(fLock);
if (fWorker != B_BAD_THREAD_ID)
return B_BUSY;
HDINFO("[Node<%s>] initiating threaded", Process()->Name());
fWorker = spawn_thread(&_StartProcess, Process()->Name(),
B_NORMAL_PRIORITY, Process());
fWorker = spawn_thread(&_RunProcessThreadEntry, Process()->Name(),
B_NORMAL_PRIORITY, this);
if (fWorker >= 0) {
resume_thread(fWorker);
@ -71,20 +95,53 @@ ThreadedProcessNode::RequestStop()
}
void
ThreadedProcessNode::_RunProcessStart()
{
if (fListener != NULL) {
if (on_exit_thread(&_RunProcessThreadExit, this) != B_OK) {
HDFATAL("unable to setup 'on exit' for thread");
}
}
AbstractProcess* process = Process();
if (process == NULL)
HDFATAL("the process node must have a process defined");
bigtime_t start = system_time();
HDINFO("[Node<%s>] starting process in thread", process->Name());
process->Run();
HDINFO("[Node<%s>] finished process in thread %f seconds", process->Name(),
(system_time() - start) / 1000000.0);
}
/*! This method is the initial function that is invoked on starting a new
thread. It will start a process that is part of the bulk-load.
*/
/*static*/ status_t
ThreadedProcessNode::_StartProcess(void* cookie)
ThreadedProcessNode::_RunProcessThreadEntry(void* cookie)
{
AbstractProcess* process = static_cast<AbstractProcess*>(cookie);
bigtime_t start = system_time();
HDINFO("[Node<%s>] starting process in thread", process->Name());
process->Run();
HDINFO("[Node<%s>] finished process in thread %f seconds", process->Name(),
(system_time() - start) / 1000000.0);
static_cast<ThreadedProcessNode*>(cookie)->_RunProcessStart();
return B_OK;
}
void
ThreadedProcessNode::_RunProcessExit()
{
AutoLocker<BLocker> locker(fLock);
fWorker = B_BAD_THREAD_ID;
HDTRACE("[Node<%s>] compute complete", Process()->Name());
if (fListener != NULL)
fListener->ProcessChanged();
}
/*static*/ void
ThreadedProcessNode::_RunProcessThreadExit(void* cookie)
{
static_cast<ThreadedProcessNode*>(cookie)->_RunProcessExit();
}

View File

@ -1,17 +1,13 @@
/*
* Copyright 2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2021-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef THREADED_PROCESS_NODE_H
#define THREADED_PROCESS_NODE_H
#include "AbstractProcessNode.h"
class AbstractProcess;
class ThreadedProcessNode : public AbstractProcessNode {
public:
ThreadedProcessNode(AbstractProcess* process,
@ -21,13 +17,16 @@ public:
virtual status_t Start();
virtual status_t RequestStop();
virtual bool IsRunning();
private:
static status_t _StartProcess(void* cookie);
void _RunProcessStart();
void _RunProcessExit();
static status_t _RunProcessThreadEntry(void* cookie);
static void _RunProcessThreadExit(void* cookie);
thread_id fWorker;
int32 fStartTimeoutSeconds;
};
#endif // THREADED_PROCESS_NODE_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -84,6 +84,7 @@ AbstractSingleFileServerProcess::RunInternal()
HDINFO("[%s] did process data", Name());
break;
default:
HDERROR("[%s] failed processing data", Name());
MoveDamagedFileAside(localPath);
break;
}
@ -97,4 +98,4 @@ status_t
AbstractSingleFileServerProcess::GetStandardMetaDataPath(BPath& path) const
{
return GetLocalPath(path);
}
}

View File

@ -20,7 +20,7 @@
IncrementViewCounterProcess::IncrementViewCounterProcess(
Model* model, const PackageInfoRef package)
Model* model, const PackageInfoRef& package)
:
fPackage(package),
fModel(model)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2021-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef INCREMENT_VIEW_COUNTER_PROCESS_H
@ -17,7 +17,7 @@ class IncrementViewCounterProcess : public AbstractProcess {
public:
IncrementViewCounterProcess(
Model* model,
const PackageInfoRef package);
const PackageInfoRef& package);
virtual ~IncrementViewCounterProcess();
const char* Name() const;

View File

@ -57,7 +57,7 @@ typedef std::map<BString, PackageInfoRef> PackageInfoMap;
*/
LocalPkgDataLoadProcess::LocalPkgDataLoadProcess(
PackageInfoListener* packageInfoListener,
PackageInfoListenerRef packageInfoListener,
Model *model, bool force)
:
AbstractProcess(),

View File

@ -32,7 +32,7 @@ class PkgDataLoadState;
class LocalPkgDataLoadProcess : public AbstractProcess {
public:
LocalPkgDataLoadProcess(
PackageInfoListener* packageInfoListener,
PackageInfoListenerRef packageInfoListener,
Model *model, bool force = false);
virtual ~LocalPkgDataLoadProcess();
@ -49,7 +49,7 @@ private:
private:
Model* fModel;
bool fForce;
PackageInfoListener*
PackageInfoListenerRef
fPackageInfoListener;
};

View File

@ -914,6 +914,9 @@ WebAppInterface::_SendRawGetRequest(const BString urlPathComponents,
{
BUrl url = ServerSettings::CreateFullUrl(urlPathComponents);
HDDEBUG("http-get; will make request to [%s]",
url.UrlString().String());
ProtocolListener listener;
BHttpHeaders headers;
@ -934,6 +937,9 @@ WebAppInterface::_SendRawGetRequest(const BString urlPathComponents,
int32 statusCode = result.StatusCode();
HDDEBUG("http-get; did receive http-status [%" B_PRId32 "] from [%s]",
statusCode, url.UrlString().String());
if (statusCode == 200)
return B_OK;

View File

@ -3,7 +3,7 @@
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Rene Gollent, rene@gollent.com.
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2016-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2016-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
@ -68,6 +68,7 @@ enum {
MSG_AUTHORIZATION_CHANGED = 'athc',
MSG_CATEGORIES_LIST_CHANGED = 'clic',
MSG_PACKAGE_CHANGED = 'pchd',
MSG_PROCESS_COORDINATOR_CHANGED = 'pccd',
MSG_WORK_STATUS_CHANGE = 'wsch',
MSG_WORK_STATUS_CLEAR = 'wscl',
@ -128,6 +129,30 @@ private:
};
class MainWindowPackageInfoListener : public PackageInfoListener {
public:
MainWindowPackageInfoListener(MainWindow* mainWindow)
:
fMainWindow(mainWindow)
{
}
~MainWindowPackageInfoListener()
{
}
private:
// PackageInfoListener
virtual void PackageChanged(const PackageInfoEvent& event)
{
fMainWindow->PackageChanged(event);
}
private:
MainWindow* fMainWindow;
};
MainWindow::MainWindow(const BMessage& settings)
:
BWindow(BRect(50, 50, 650, 550), B_TRANSLATE_SYSTEM_NAME("HaikuDepot"),
@ -147,6 +172,9 @@ MainWindow::MainWindow(const BMessage& settings)
if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK)
debugger("unable to create the process coordinator semaphore");
fPackageInfoListener = PackageInfoListenerRef(
new MainWindowPackageInfoListener(this), true);
BMenuBar* menuBar = new BMenuBar("Main Menu");
_BuildMenu(menuBar);
@ -242,6 +270,9 @@ MainWindow::MainWindow(const BMessage& settings, PackageInfoRef& package)
if ((fCoordinatorRunningSem = create_sem(1, "ProcessCoordinatorSem")) < B_OK)
debugger("unable to create the process coordinator semaphore");
fPackageInfoListener = PackageInfoListenerRef(
new MainWindowPackageInfoListener(this), true);
fFilterView = new FilterView();
fPackageInfoView = new PackageInfoView(&fModel, this);
fWorkStatusView = new WorkStatusView("work status");
@ -317,7 +348,7 @@ MainWindow::QuitRequested()
AutoLocker<BLocker> lock(&fCoordinatorLock);
fShouldCloseWhenNoProcessesToCoordinate = true;
if (fCoordinator.IsSet()) {
if (fCoordinator != NULL) {
HDINFO("a coordinator is running --> will wait before quitting...");
if (fShuttingDownWindow == NULL)
@ -545,6 +576,13 @@ MainWindow::MessageReceived(BMessage* message)
break;
}
case MSG_PROCESS_COORDINATOR_CHANGED:
{
ProcessCoordinatorState state(message);
_HandleProcessCoordinatorChanged(state);
break;
}
case MSG_RATE_PACKAGE:
_RatePackage();
break;
@ -927,10 +965,8 @@ MainWindow::_AddRemovePackageFromLists(const PackageInfoRef& package)
void
MainWindow::_IncrementViewCounter(const PackageInfoRef& package)
MainWindow::_IncrementViewCounter(const PackageInfoRef package)
{
// Temporarily disabled, see tickets #16879 and #17689.
#if 0
bool shouldIncrementViewCounter = false;
{
@ -948,7 +984,6 @@ MainWindow::_IncrementViewCounter(const PackageInfoRef& package)
&fModel, package);
_AddProcessCoordinator(incrementViewCoordinator);
}
#endif
}
@ -988,9 +1023,7 @@ MainWindow::_StartBulkLoad(bool force)
fRefreshRepositoriesItem->SetEnabled(false);
ProcessCoordinator* bulkLoadCoordinator =
ProcessCoordinatorFactory::CreateBulkLoadCoordinator(
this,
// PackageInfoListener
&fModel, force);
fPackageInfoListener, &fModel, force);
_AddProcessCoordinator(bulkLoadCoordinator);
}
@ -1422,7 +1455,6 @@ MainWindow::_HandleUserUsageConditionsNotLatest(
void
MainWindow::_AddProcessCoordinator(ProcessCoordinator* item)
{
BReference<ProcessCoordinator> itemRef(item, true);
AutoLocker<BLocker> lock(&fCoordinatorLock);
if (fShouldCloseWhenNoProcessesToCoordinate) {
@ -1433,12 +1465,13 @@ MainWindow::_AddProcessCoordinator(ProcessCoordinator* item)
item->SetListener(this);
if (!fCoordinator.IsSet()) {
if (fCoordinator == NULL) {
if (acquire_sem(fCoordinatorRunningSem) != B_OK)
debugger("unable to acquire the process coordinator sem");
HDINFO("adding and starting a process coordinator [%s]",
item->Name().String());
fCoordinator = itemRef;
delete fCoordinator;
fCoordinator = item;
fCoordinator->Start();
} else {
HDINFO("adding process coordinator [%s] to the queue",
@ -1458,7 +1491,7 @@ MainWindow::_SpinUntilProcessCoordinatorComplete()
debugger("unable to release the process coordinator sem");
{
AutoLocker<BLocker> lock(&fCoordinatorLock);
if (!fCoordinator.IsSet())
if (fCoordinator == NULL)
return;
}
}
@ -1472,14 +1505,15 @@ MainWindow::_StopProcessCoordinators()
AutoLocker<BLocker> lock(&fCoordinatorLock);
while (!fCoordinatorQueue.empty()) {
BReference<ProcessCoordinator> processCoordinator
ProcessCoordinator* processCoordinator
= fCoordinatorQueue.front();
HDINFO("will drop queued process coordinator [%s]",
processCoordinator->Name().String());
fCoordinatorQueue.pop();
delete processCoordinator;
}
if (fCoordinator.IsSet())
if (fCoordinator != NULL)
fCoordinator->RequestStop();
}
@ -1492,10 +1526,23 @@ MainWindow::_StopProcessCoordinators()
void
MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState)
{
BMessage message(MSG_PROCESS_COORDINATOR_CHANGED);
if (coordinatorState.Archive(&message, true) != B_OK) {
HDFATAL("unable to archive message when the process coordinator"
" has changed");
}
BMessenger(this).SendMessage(&message);
}
void
MainWindow::_HandleProcessCoordinatorChanged(ProcessCoordinatorState& coordinatorState)
{
AutoLocker<BLocker> lock(&fCoordinatorLock);
if (fCoordinator.Get() == coordinatorState.Coordinator()) {
if (fCoordinator->Identifier()
== coordinatorState.ProcessCoordinatorIdentifier()) {
if (!coordinatorState.IsRunning()) {
if (release_sem(fCoordinatorRunningSem) != B_OK)
debugger("unable to release the process coordinator sem");
@ -1511,9 +1558,8 @@ MainWindow::CoordinatorChanged(ProcessCoordinatorState& coordinatorState)
messenger.SendMessage(message);
}
fCoordinator = BReference<ProcessCoordinator>(NULL);
// will delete the old process coordinator if it is not used
// elsewhere.
delete fCoordinator;
fCoordinator = NULL;
// now schedule the next one.
if (!fCoordinatorQueue.empty()) {

View File

@ -2,7 +2,7 @@
* Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
* Copyright 2013, Rene Gollent <rene@gollent.com>.
* Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
* Copyright 2017-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* Copyright 2017-2022, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef MAIN_WINDOW_H
@ -35,7 +35,7 @@ class ShuttingDownWindow;
class WorkStatusView;
class MainWindow : private PackageInfoListener,
class MainWindow :
private ProcessCoordinatorConsumer, public ProcessCoordinatorListener,
public UserDetailVerifierListener, public BWindow {
public:
@ -61,9 +61,9 @@ public:
virtual void UserCredentialsFailed();
virtual void UserUsageConditionsNotLatest(
const UserDetail& userDetail);
private:
// PackageInfoListener
virtual void PackageChanged(
// services PackageInfoListener via MainWindowPackageInfoListener
void PackageChanged(
const PackageInfoEvent& event);
private:
@ -99,7 +99,7 @@ private:
void _ClearPackage();
void _IncrementViewCounter(
const PackageInfoRef& package);
const PackageInfoRef package);
void _PopulatePackageAsync(bool forcePopulate);
void _StartBulkLoad(bool force = false);
@ -118,6 +118,9 @@ private:
void _HandleChangePackageListViewMode();
void _HandleProcessCoordinatorChanged(
ProcessCoordinatorState& coordinatorState);
static status_t _RefreshModelThreadWorker(void* arg);
static status_t _PopulatePackageWorker(void* arg);
static status_t _PackagesToShowWorker(void* arg);
@ -165,10 +168,9 @@ private:
Model fModel;
ModelListenerRef fModelListener;
std::queue<BReference<ProcessCoordinator> >
std::queue<ProcessCoordinator*>
fCoordinatorQueue;
BReference<ProcessCoordinator>
fCoordinator;
ProcessCoordinator* fCoordinator;
BLocker fCoordinatorLock;
sem_id fCoordinatorRunningSem;
bool fShouldCloseWhenNoProcessesToCoordinate;
@ -180,6 +182,11 @@ private:
bool fForcePopulatePackage;
BLocker fPackageToPopulateLock;
sem_id fPackageToPopulateSem;
PackageInfoListenerRef
fPackageInfoListener;
};
#endif // MAIN_WINDOW_H