Implemented a generic system watching mechanism with a userland API. Currently
only a few events can be watched (team creation/deletion/exec, thread creation/ deletion/name changes). The functions start_system_watching()/ stop_system_watching start/stop watching events. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39862 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
|
||||
struct kernel_args;
|
||||
|
||||
|
||||
@@ -15,15 +16,24 @@ struct kernel_args;
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern status_t system_info_init(struct kernel_args *args);
|
||||
extern uint32 get_haiku_revision(void);
|
||||
|
||||
extern status_t _user_get_system_info(system_info *userInfo, size_t size);
|
||||
extern status_t _user_get_system_info_etc(int32 id, void *buffer,
|
||||
size_t bufferSize);
|
||||
status_t system_info_init(struct kernel_args *args);
|
||||
status_t system_notifications_init();
|
||||
uint32 get_haiku_revision(void);
|
||||
|
||||
status_t _user_get_system_info(system_info *userInfo, size_t size);
|
||||
status_t _user_get_system_info_etc(int32 id, void *buffer,
|
||||
size_t bufferSize);
|
||||
|
||||
status_t _user_start_system_watching(int32 object, uint32 flags,
|
||||
port_id port, int32 token);
|
||||
status_t _user_stop_system_watching(int32 object, uint32 flags,
|
||||
port_id port, int32 token);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _KERNEL_SYSTEM_INFO_H */
|
||||
|
||||
@@ -174,6 +174,11 @@ extern status_t _kern_get_team_usage_info(team_id team, int32 who,
|
||||
extern status_t _kern_get_extended_team_info(team_id teamID, uint32 flags,
|
||||
void* buffer, size_t size, size_t* _sizeNeeded);
|
||||
|
||||
extern status_t _kern_start_system_watching(int32 object, uint32 flags,
|
||||
port_id port, int32 token);
|
||||
extern status_t _kern_stop_system_watching(int32 object, uint32 flags,
|
||||
port_id port, int32 token);
|
||||
|
||||
extern status_t _kern_block_thread(uint32 flags, bigtime_t timeout);
|
||||
extern status_t _kern_unblock_thread(thread_id thread, status_t status);
|
||||
extern status_t _kern_unblock_threads(thread_id* threads, uint32 count,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008 Haiku, Inc. All rights reserved.
|
||||
* Copyright 2008-2010, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _SYSTEM_INFO_H
|
||||
@@ -24,14 +24,56 @@ struct system_memory_info {
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
// team creation or deletion; object == -1; either one also triggers on
|
||||
// exec()
|
||||
B_WATCH_SYSTEM_TEAM_CREATION = 0x01,
|
||||
B_WATCH_SYSTEM_TEAM_DELETION = 0x02,
|
||||
|
||||
// thread creation or deletion or property (name, priority) changes;
|
||||
// object == team ID or -1 for all teams
|
||||
B_WATCH_SYSTEM_THREAD_CREATION = 0x04,
|
||||
B_WATCH_SYSTEM_THREAD_DELETION = 0x08,
|
||||
B_WATCH_SYSTEM_THREAD_PROPERTIES = 0x10,
|
||||
|
||||
B_WATCH_SYSTEM_ALL
|
||||
= B_WATCH_SYSTEM_TEAM_CREATION
|
||||
| B_WATCH_SYSTEM_TEAM_DELETION
|
||||
| B_WATCH_SYSTEM_THREAD_CREATION
|
||||
| B_WATCH_SYSTEM_THREAD_DELETION
|
||||
| B_WATCH_SYSTEM_THREAD_PROPERTIES
|
||||
};
|
||||
|
||||
enum {
|
||||
// message what for the notification messages
|
||||
B_SYSTEM_OBJECT_UPDATE = 'SOUP',
|
||||
|
||||
// "opcode" values
|
||||
B_TEAM_CREATED = 0,
|
||||
B_TEAM_DELETED = 1,
|
||||
B_TEAM_EXEC = 2,
|
||||
B_THREAD_CREATED = 3,
|
||||
B_THREAD_DELETED = 4,
|
||||
B_THREAD_NAME_CHANGED = 5
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern status_t get_system_info_etc(int32 id, void *buffer, size_t bufferSize);
|
||||
|
||||
status_t get_system_info_etc(int32 id, void* buffer, size_t bufferSize);
|
||||
|
||||
status_t start_system_watching(int32 object, uint32 flags, port_id port,
|
||||
int32 token);
|
||||
status_t stop_system_watching(int32 object, uint32 flags, port_id port,
|
||||
int32 token);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _SYSTEM_INFO_H */
|
||||
|
||||
@@ -270,6 +270,9 @@ main2(void *unused)
|
||||
TRACE("init user mutex\n");
|
||||
user_mutex_init();
|
||||
|
||||
TRACE("init system notifications\n");
|
||||
system_notifications_init();
|
||||
|
||||
TRACE("Init modules\n");
|
||||
boot_splash_set_stage(BOOT_SPLASH_STAGE_1_INIT_MODULES);
|
||||
module_init_post_threads();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Authors:
|
||||
* Stefano Ceccherini
|
||||
* Axel Dörfler, axeld@pinc-software.de
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
|
||||
|
||||
@@ -17,16 +18,22 @@
|
||||
#include <OS.h>
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include <block_cache.h>
|
||||
#include <cpu.h>
|
||||
#include <debug.h>
|
||||
#include <kernel.h>
|
||||
#include <lock.h>
|
||||
#include <Notifications.h>
|
||||
#include <messaging.h>
|
||||
#include <port.h>
|
||||
#include <real_time_clock.h>
|
||||
#include <sem.h>
|
||||
#include <smp.h>
|
||||
#include <team.h>
|
||||
#include <thread.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
@@ -66,6 +73,335 @@ dump_info(int argc, char **argv)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - user notifications
|
||||
|
||||
|
||||
class SystemNotificationService : private NotificationListener {
|
||||
public:
|
||||
SystemNotificationService()
|
||||
{
|
||||
mutex_init(&fLock, "system notification service");
|
||||
}
|
||||
|
||||
status_t Init()
|
||||
{
|
||||
status_t error = fTeamListeners.Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
error = NotificationManager::Manager().AddListener("teams",
|
||||
TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
error = NotificationManager::Manager().AddListener("threads",
|
||||
THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t StartListening(int32 object, uint32 flags, port_id port,
|
||||
int32 token)
|
||||
{
|
||||
// check the parameters
|
||||
if ((object < 0 && object != -1) || port < 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if ((flags & B_WATCH_SYSTEM_ALL) == 0
|
||||
|| (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
// maybe the listener already exists
|
||||
ListenerList* listenerList;
|
||||
Listener* listener = _FindListener(object, port, token, listenerList);
|
||||
if (listener != NULL) {
|
||||
// just add the new flags
|
||||
listener->flags |= flags;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// create a new listener
|
||||
listener = new(std::nothrow) Listener;
|
||||
if (listener == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ObjectDeleter<Listener> listenerDeleter(listener);
|
||||
|
||||
listener->port = port;
|
||||
listener->token = token;
|
||||
listener->flags = flags;
|
||||
|
||||
// if there's no list yet, create a new list
|
||||
if (listenerList == NULL) {
|
||||
listenerList = new(std::nothrow) ListenerList;
|
||||
if (listenerList == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
listenerList->object = object;
|
||||
|
||||
fTeamListeners.Insert(listenerList);
|
||||
}
|
||||
|
||||
listener->list = listenerList;
|
||||
listenerList->listeners.Add(listener);
|
||||
listenerDeleter.Detach();
|
||||
|
||||
team_associate_data(listener);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t StopListening(int32 object, uint32 flags, port_id port,
|
||||
int32 token)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
// find the listener
|
||||
ListenerList* listenerList;
|
||||
Listener* listener = _FindListener(object, port, token, listenerList);
|
||||
if (listener == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
// clear the given flags
|
||||
listener->flags &= ~flags;
|
||||
|
||||
if (listener->flags != 0)
|
||||
return B_OK;
|
||||
|
||||
team_dissociate_data(listener);
|
||||
_RemoveListener(listener);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
struct ListenerList;
|
||||
|
||||
struct Listener : AssociatedData {
|
||||
DoublyLinkedListLink<Listener> listLink;
|
||||
ListenerList* list;
|
||||
port_id port;
|
||||
int32 token;
|
||||
uint32 flags;
|
||||
|
||||
virtual void OwnerDeleted(AssociatedDataOwner* owner);
|
||||
};
|
||||
|
||||
friend struct Listener;
|
||||
|
||||
struct ListenerList {
|
||||
typedef DoublyLinkedList<Listener,
|
||||
DoublyLinkedListMemberGetLink<Listener, &Listener::listLink> > List;
|
||||
|
||||
ListenerList* hashNext;
|
||||
List listeners;
|
||||
int32 object;
|
||||
};
|
||||
|
||||
struct ListenerHashDefinition {
|
||||
typedef int32 KeyType;
|
||||
typedef ListenerList ValueType;
|
||||
|
||||
size_t HashKey(int32 key) const
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
size_t Hash(const ListenerList* value) const
|
||||
{
|
||||
return HashKey(value->object);
|
||||
}
|
||||
|
||||
bool Compare(int32 key, const ListenerList* value) const
|
||||
{
|
||||
return value->object == key;
|
||||
}
|
||||
|
||||
ListenerList*& GetLink(ListenerList* value) const
|
||||
{
|
||||
return value->hashNext;
|
||||
}
|
||||
};
|
||||
|
||||
typedef BOpenHashTable<ListenerHashDefinition> ListenerHash;
|
||||
|
||||
private:
|
||||
virtual void EventOccurred(NotificationService& service,
|
||||
const KMessage* event)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
int32 eventCode;
|
||||
int32 teamID;
|
||||
if (event->FindInt32("event", &eventCode) != B_OK
|
||||
|| event->FindInt32("team", &teamID) != B_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32 object;
|
||||
uint32 opcode;
|
||||
uint32 flags;
|
||||
|
||||
// translate the event
|
||||
if (event->What() == TEAM_MONITOR) {
|
||||
switch (eventCode) {
|
||||
case TEAM_ADDED:
|
||||
opcode = B_TEAM_CREATED;
|
||||
flags = B_WATCH_SYSTEM_TEAM_CREATION;
|
||||
break;
|
||||
case TEAM_REMOVED:
|
||||
opcode = B_TEAM_DELETED;
|
||||
flags = B_WATCH_SYSTEM_TEAM_DELETION;
|
||||
break;
|
||||
case TEAM_EXEC:
|
||||
opcode = B_TEAM_EXEC;
|
||||
flags = B_WATCH_SYSTEM_TEAM_CREATION
|
||||
| B_WATCH_SYSTEM_TEAM_DELETION;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
object = teamID;
|
||||
} else if (event->What() == THREAD_MONITOR) {
|
||||
if (event->FindInt32("thread", &object) != B_OK)
|
||||
return;
|
||||
|
||||
switch (eventCode) {
|
||||
case THREAD_ADDED:
|
||||
opcode = B_THREAD_CREATED;
|
||||
flags = B_WATCH_SYSTEM_THREAD_CREATION;
|
||||
break;
|
||||
case THREAD_REMOVED:
|
||||
opcode = B_THREAD_DELETED;
|
||||
flags = B_WATCH_SYSTEM_THREAD_DELETION;
|
||||
break;
|
||||
case THREAD_NAME_CHANGED:
|
||||
opcode = B_THREAD_NAME_CHANGED;
|
||||
flags = B_WATCH_SYSTEM_THREAD_PROPERTIES;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else
|
||||
return;
|
||||
|
||||
// find matching listeners
|
||||
messaging_target targets[kMaxMessagingTargetCount];
|
||||
int32 targetCount = 0;
|
||||
|
||||
_AddTargets(fTeamListeners.Lookup(teamID), flags, targets,
|
||||
targetCount, object, opcode);
|
||||
_AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount,
|
||||
object, opcode);
|
||||
|
||||
// send the message
|
||||
if (targetCount > 0)
|
||||
_SendMessage(targets, targetCount, teamID, opcode);
|
||||
}
|
||||
|
||||
void _AddTargets(ListenerList* listenerList, uint32 flags,
|
||||
messaging_target* targets, int32& targetCount, int32 object,
|
||||
uint32 opcode)
|
||||
{
|
||||
if (listenerList == NULL)
|
||||
return;
|
||||
|
||||
for (ListenerList::List::Iterator it
|
||||
= listenerList->listeners.GetIterator();
|
||||
Listener* listener = it.Next();) {
|
||||
if ((listener->flags & flags) == 0)
|
||||
continue;
|
||||
|
||||
// array is full -- need to flush it first
|
||||
if (targetCount == kMaxMessagingTargetCount) {
|
||||
_SendMessage(targets, targetCount, object, opcode);
|
||||
targetCount = 0;
|
||||
}
|
||||
|
||||
// add the listener
|
||||
targets[targetCount].port = listener->port;
|
||||
targets[targetCount++].token = listener->token;
|
||||
}
|
||||
}
|
||||
|
||||
void _SendMessage(messaging_target* targets, int32 targetCount,
|
||||
int32 object, uint32 opcode)
|
||||
{
|
||||
// prepare the message
|
||||
char buffer[128];
|
||||
KMessage message;
|
||||
message.SetTo(buffer, sizeof(buffer), B_SYSTEM_OBJECT_UPDATE);
|
||||
message.AddInt32("opcode", opcode);
|
||||
if (opcode < B_THREAD_CREATED)
|
||||
message.AddInt32("team", object);
|
||||
else
|
||||
message.AddInt32("thread", object);
|
||||
|
||||
// send it
|
||||
send_message(message.Buffer(), message.ContentSize(), targets,
|
||||
targetCount);
|
||||
}
|
||||
|
||||
Listener* _FindListener(int32 object, port_id port, int32 token,
|
||||
ListenerList*& _listenerList)
|
||||
{
|
||||
_listenerList = fTeamListeners.Lookup(object);
|
||||
if (_listenerList == NULL)
|
||||
return NULL;
|
||||
|
||||
for (ListenerList::List::Iterator it
|
||||
= _listenerList->listeners.GetIterator();
|
||||
Listener* listener = it.Next();) {
|
||||
if (listener->port == port && listener->token == token)
|
||||
return listener;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _RemoveObsoleteListener(Listener* listener)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
_RemoveListener(listener);
|
||||
}
|
||||
|
||||
void _RemoveListener(Listener* listener)
|
||||
{
|
||||
// no flags anymore -- remove the listener
|
||||
ListenerList* listenerList = listener->list;
|
||||
listenerList->listeners.Remove(listener);
|
||||
listener->ReleaseReference();
|
||||
|
||||
if (listenerList->listeners.IsEmpty()) {
|
||||
// no listeners in the list anymore -- remove the list from the hash
|
||||
// table
|
||||
fTeamListeners.Remove(listenerList);
|
||||
delete listenerList;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const int32 kMaxMessagingTargetCount = 8;
|
||||
|
||||
mutex fLock;
|
||||
ListenerHash fTeamListeners;
|
||||
};
|
||||
|
||||
static SystemNotificationService sSystemNotificationService;
|
||||
|
||||
|
||||
void
|
||||
SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner)
|
||||
{
|
||||
sSystemNotificationService._RemoveObsoleteListener(this);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
@@ -115,6 +451,21 @@ system_info_init(struct kernel_args *args)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
system_notifications_init()
|
||||
{
|
||||
new (&sSystemNotificationService) SystemNotificationService;
|
||||
|
||||
status_t error = sSystemNotificationService.Init();
|
||||
if (error != B_OK) {
|
||||
panic("system_info_init(): Failed to init system notification service");
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
get_haiku_revision(void)
|
||||
{
|
||||
@@ -170,3 +521,20 @@ _user_get_system_info_etc(int32 id, void* userInfo, size_t size)
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_start_system_watching(int32 object, uint32 flags, port_id port,
|
||||
int32 token)
|
||||
{
|
||||
return sSystemNotificationService.StartListening(object, flags, port,
|
||||
token);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_stop_system_watching(int32 object, uint32 flags, port_id port,
|
||||
int32 token)
|
||||
{
|
||||
return sSystemNotificationService.StopListening(object, flags, port, token);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,20 @@ get_system_info_etc(int32 id, void *info, size_t size)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
start_system_watching(int32 object, uint32 flags, port_id port, int32 token)
|
||||
{
|
||||
return _kern_start_system_watching(object, flags, port, token);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
stop_system_watching(int32 object, uint32 flags, port_id port, int32 token)
|
||||
{
|
||||
return _kern_stop_system_watching(object, flags, port, token);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
is_computer_on(void)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user