* 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:
Axel Dörfler
2009-08-19 17:09:33 +00:00
parent 87da7984dc
commit 6d908f83f3
@@ -54,6 +54,16 @@ static const char* kFileSystemPrefix = "file_systems";
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 {
inline partition_id operator()(const KPartition* partition) const
{
@@ -119,9 +129,7 @@ private:
class KDiskDeviceManager::DeviceWatcher : public NotificationListener {
public:
DeviceWatcher(KDiskDeviceManager* manager)
:
fManager(manager)
DeviceWatcher()
{
}
@@ -132,45 +140,34 @@ public:
virtual void EventOccured(NotificationService& service,
const KMessage* event)
{
int32 opCode = event->GetInt32("opcode", -1);
switch (opCode) {
int32 opcode = event->GetInt32("opcode", -1);
switch (opcode) {
case B_ENTRY_CREATED:
case B_ENTRY_REMOVED:
{
const char* name = event->GetString("name", "");
dev_t device = event->GetInt32("device", -1);
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)
device_event* deviceEvent = new(std::nothrow) device_event;
if (deviceEvent == NULL)
break;
if (S_ISDIR(st.st_mode)) {
if (opCode == B_ENTRY_CREATED)
add_node_listener(device, node, B_WATCH_DIRECTORY,
*this);
else
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();
const char* name = event->GetString("name", NULL);
if (name != NULL)
deviceEvent->name = strdup(name);
else
deviceEvent->name = NULL;
if (opCode == B_ENTRY_CREATED)
fManager->CreateDevice(path.Path());
else
fManager->DeleteDevice(path.Path());
}
}
deviceEvent->opcode = opcode;
deviceEvent->device = event->GetInt32("device", -1);
deviceEvent->directory = event->GetInt64("directory", -1);
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;
}
@@ -179,8 +176,46 @@ public:
}
}
private:
KDiskDeviceManager* fManager;
static status_t _HandleDeviceEvent(void* _event)
{
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),
fTerminating(false),
fDiskSystemWatcher(NULL),
fDeviceWatcher(new(nothrow) DeviceWatcher(this)),
fDeviceWatcher(new(nothrow) DeviceWatcher()),
fNotifications(new(nothrow) DiskNotifications)
{
if (InitCheck() != B_OK)