* Must be careful which functions to call in the notifications handler, or else
you can easily produce a dead lock. This should fix the system hang experienced as part of bug #4223. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@32526 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -54,6 +54,16 @@ static const char* kFileSystemPrefix = "file_systems";
|
|||||||
KDiskDeviceManager* KDiskDeviceManager::sDefaultManager = NULL;
|
KDiskDeviceManager* KDiskDeviceManager::sDefaultManager = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
struct device_event {
|
||||||
|
int32 opcode;
|
||||||
|
const char* name;
|
||||||
|
dev_t device;
|
||||||
|
ino_t directory;
|
||||||
|
ino_t node;
|
||||||
|
NotificationListener* listener;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct GetPartitionID {
|
struct GetPartitionID {
|
||||||
inline partition_id operator()(const KPartition* partition) const
|
inline partition_id operator()(const KPartition* partition) const
|
||||||
{
|
{
|
||||||
@@ -119,9 +129,7 @@ private:
|
|||||||
|
|
||||||
class KDiskDeviceManager::DeviceWatcher : public NotificationListener {
|
class KDiskDeviceManager::DeviceWatcher : public NotificationListener {
|
||||||
public:
|
public:
|
||||||
DeviceWatcher(KDiskDeviceManager* manager)
|
DeviceWatcher()
|
||||||
:
|
|
||||||
fManager(manager)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,45 +140,34 @@ public:
|
|||||||
virtual void EventOccured(NotificationService& service,
|
virtual void EventOccured(NotificationService& service,
|
||||||
const KMessage* event)
|
const KMessage* event)
|
||||||
{
|
{
|
||||||
int32 opCode = event->GetInt32("opcode", -1);
|
int32 opcode = event->GetInt32("opcode", -1);
|
||||||
switch (opCode) {
|
switch (opcode) {
|
||||||
case B_ENTRY_CREATED:
|
case B_ENTRY_CREATED:
|
||||||
case B_ENTRY_REMOVED:
|
case B_ENTRY_REMOVED:
|
||||||
{
|
{
|
||||||
const char* name = event->GetString("name", "");
|
device_event* deviceEvent = new(std::nothrow) device_event;
|
||||||
dev_t device = event->GetInt32("device", -1);
|
if (deviceEvent == NULL)
|
||||||
ino_t directory = event->GetInt64("directory", -1);
|
|
||||||
ino_t node = event->GetInt64("node", -1);
|
|
||||||
|
|
||||||
struct stat st;
|
|
||||||
if (vfs_stat_node_ref(device, node, &st) != 0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (S_ISDIR(st.st_mode)) {
|
const char* name = event->GetString("name", NULL);
|
||||||
if (opCode == B_ENTRY_CREATED)
|
if (name != NULL)
|
||||||
add_node_listener(device, node, B_WATCH_DIRECTORY,
|
deviceEvent->name = strdup(name);
|
||||||
*this);
|
else
|
||||||
else
|
deviceEvent->name = NULL;
|
||||||
remove_node_listener(device, node, *this);
|
|
||||||
} else {
|
|
||||||
if (strcmp(name, "raw") == 0) {
|
|
||||||
// a new raw device was added/removed
|
|
||||||
KPath path(B_PATH_NAME_LENGTH + 1);
|
|
||||||
if (path.InitCheck() != B_OK
|
|
||||||
|| vfs_entry_ref_to_path(device, directory,
|
|
||||||
name, path.LockBuffer(),
|
|
||||||
path.BufferSize()) != B_OK) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
path.UnlockBuffer();
|
|
||||||
|
|
||||||
if (opCode == B_ENTRY_CREATED)
|
deviceEvent->opcode = opcode;
|
||||||
fManager->CreateDevice(path.Path());
|
deviceEvent->device = event->GetInt32("device", -1);
|
||||||
else
|
deviceEvent->directory = event->GetInt64("directory", -1);
|
||||||
fManager->DeleteDevice(path.Path());
|
deviceEvent->node = event->GetInt64("node", -1);
|
||||||
}
|
deviceEvent->listener = this;
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: a real in-kernel DPC mechanism would be preferred...
|
||||||
|
thread_id thread = spawn_kernel_thread(_HandleDeviceEvent,
|
||||||
|
"device event", B_NORMAL_PRIORITY, deviceEvent);
|
||||||
|
if (thread < 0)
|
||||||
|
delete deviceEvent;
|
||||||
|
else
|
||||||
|
resume_thread(thread);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,8 +176,46 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
static status_t _HandleDeviceEvent(void* _event)
|
||||||
KDiskDeviceManager* fManager;
|
{
|
||||||
|
device_event* event = (device_event*)_event;
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (vfs_stat_node_ref(event->device, event->node, &st) != 0) {
|
||||||
|
delete event;
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(st.st_mode)) {
|
||||||
|
if (event->opcode == B_ENTRY_CREATED) {
|
||||||
|
add_node_listener(event->device, event->node, B_WATCH_DIRECTORY,
|
||||||
|
*event->listener);
|
||||||
|
} else {
|
||||||
|
remove_node_listener(event->device, event->node,
|
||||||
|
*event->listener);
|
||||||
|
}
|
||||||
|
} else if (strcmp(event->name, "raw") == 0) {
|
||||||
|
// a new raw device was added/removed
|
||||||
|
KPath path(B_PATH_NAME_LENGTH + 1);
|
||||||
|
if (path.InitCheck() != B_OK
|
||||||
|
|| vfs_entry_ref_to_path(event->device, event->directory,
|
||||||
|
event->name, path.LockBuffer(),
|
||||||
|
path.BufferSize()) != B_OK) {
|
||||||
|
delete event;
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
path.UnlockBuffer();
|
||||||
|
|
||||||
|
if (event->opcode == B_ENTRY_CREATED)
|
||||||
|
KDiskDeviceManager::Default()->CreateDevice(path.Path());
|
||||||
|
else
|
||||||
|
KDiskDeviceManager::Default()->DeleteDevice(path.Path());
|
||||||
|
}
|
||||||
|
|
||||||
|
delete event;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -211,7 +246,7 @@ KDiskDeviceManager::KDiskDeviceManager()
|
|||||||
fMediaChecker(-1),
|
fMediaChecker(-1),
|
||||||
fTerminating(false),
|
fTerminating(false),
|
||||||
fDiskSystemWatcher(NULL),
|
fDiskSystemWatcher(NULL),
|
||||||
fDeviceWatcher(new(nothrow) DeviceWatcher(this)),
|
fDeviceWatcher(new(nothrow) DeviceWatcher()),
|
||||||
fNotifications(new(nothrow) DiskNotifications)
|
fNotifications(new(nothrow) DiskNotifications)
|
||||||
{
|
{
|
||||||
if (InitCheck() != B_OK)
|
if (InitCheck() != B_OK)
|
||||||
|
|||||||
Reference in New Issue
Block a user