From: Rémi Bernon rbernon@codeweavers.com
--- server/file.h | 3 ++ server/mapping.c | 106 ++++++++++++++++++++++++++++++++++++++++++++ server/protocol.def | 7 ++- server/user.h | 1 + server/winstation.c | 7 +++ 5 files changed, 123 insertions(+), 1 deletion(-)
diff --git a/server/file.h b/server/file.h index 661e8f9d159..abfc2f0a2ce 100644 --- a/server/file.h +++ b/server/file.h @@ -192,6 +192,9 @@ extern struct mapping *create_session_mapping( struct object *root, const struct unsigned int attr, const struct security_descriptor *sd ); extern void set_session_mapping( struct mapping *mapping );
+extern const volatile void *alloc_shared_object(void); +extern void free_shared_object( const volatile void *object_shm ); + #define SHARED_WRITE_BEGIN( object_shm, type ) \ do { \ const type *__shared = (object_shm); \ diff --git a/server/mapping.c b/server/mapping.c index 6605c111beb..f77de343f4f 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -229,17 +229,30 @@ struct session_block { struct list entry; /* entry in the session block list */ const char *data; /* base pointer for the mmaped data */ + mem_size_t offset; /* offset of data in the session shared mapping */ + mem_size_t used_size; /* used size for previously allocated objects */ + mem_size_t block_size; /* total size of the block */ +}; + +struct session_object +{ + struct list entry; /* entry in the session free object list */ + mem_size_t offset; /* offset of obj in the session shared mapping */ + shared_object_t obj; /* object actually shared with the client */ };
struct session { struct list blocks; + struct list free_objects; + object_id_t last_object_id; };
static struct mapping *session_mapping; static struct session session = { .blocks = LIST_INIT(session.blocks), + .free_objects = LIST_INIT(session.free_objects), };
#define ROUND_SIZE(size) (((size) + page_mask) & ~page_mask) @@ -1297,10 +1310,103 @@ void set_session_mapping( struct mapping *mapping ) }
block->data = tmp; + block->offset = 0; + block->used_size = 0; + block->block_size = size; + session_mapping = mapping; list_add_tail( &session.blocks, &block->entry ); }
+static struct session_block *grow_session_mapping( mem_size_t needed ) +{ + mem_size_t old_size = session_mapping->size, new_size; + struct session_block *block; + int unix_fd; + void *tmp; + + new_size = max( old_size * 3 / 2, old_size + max( needed, 0x10000 ) ); + new_size = (new_size + page_mask) & ~((mem_size_t)page_mask); + assert( new_size > old_size ); + + unix_fd = get_unix_fd( session_mapping->fd ); + if (!grow_file( unix_fd, new_size )) return NULL; + + if (!(block = mem_alloc( sizeof(*block) ))) return NULL; + if ((tmp = mmap( NULL, new_size - old_size, PROT_READ | PROT_WRITE, MAP_SHARED, unix_fd, old_size )) == MAP_FAILED) + { + file_set_error(); + free( block ); + return NULL; + } + + block->data = tmp; + block->offset = old_size; + block->used_size = 0; + block->block_size = new_size - old_size; + + session_mapping->size = new_size; + list_add_tail( &session.blocks, &block->entry ); + + return block; +} + +static struct session_block *find_free_session_block( mem_size_t size ) +{ + struct session_block *block; + + LIST_FOR_EACH_ENTRY( block, &session.blocks, struct session_block, entry ) + if (size < block->block_size && block->used_size < block->block_size - size) return block; + + return grow_session_mapping( size ); +} + +const volatile void *alloc_shared_object(void) +{ + struct session_object *object; + struct list *ptr; + + if ((ptr = list_head( &session.free_objects ))) + { + object = CONTAINING_RECORD( ptr, struct session_object, entry ); + list_remove( &object->entry ); + } + else + { + mem_size_t size = sizeof(*object); + struct session_block *block; + + if (!(block = find_free_session_block( size ))) return NULL; + object = (struct session_object *)(block->data + block->used_size); + object->offset = (char *)&object->obj - block->data; + block->used_size += size; + } + + SHARED_WRITE_BEGIN( &object->obj.shm, object_shm_t ) + { + /* mark the object data as uninitialized */ + mark_block_uninitialized( (void *)shared, sizeof(*shared) ); + CONTAINING_RECORD( shared, shared_object_t, shm )->id = ++session.last_object_id; + } + SHARED_WRITE_END; + + return &object->obj.shm; +} + +void free_shared_object( const volatile void *object_shm ) +{ + struct session_object *object = CONTAINING_RECORD( object_shm, struct session_object, obj.shm ); + + SHARED_WRITE_BEGIN( &object->obj.shm, object_shm_t ) + { + mark_block_noaccess( (void *)shared, sizeof(*shared) ); + CONTAINING_RECORD( shared, shared_object_t, shm )->id = 0; + } + SHARED_WRITE_END; + + list_add_tail( &session.free_objects, &object->entry ); +} + struct object *create_user_data_mapping( struct object *root, const struct unicode_str *name, unsigned int attr, const struct security_descriptor *sd ) { diff --git a/server/protocol.def b/server/protocol.def index 832dfea752b..aad5d1f2446 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -899,9 +899,14 @@ struct directory_entry /****************************************************************/ /* shared session mapping structures */
-typedef volatile union +typedef volatile struct { char placeholder; +} desktop_shm_t; + +typedef volatile union +{ + desktop_shm_t desktop; } object_shm_t;
typedef volatile struct diff --git a/server/user.h b/server/user.h index 8ff837a34bf..cb68e4fc314 100644 --- a/server/user.h +++ b/server/user.h @@ -93,6 +93,7 @@ struct desktop struct global_cursor cursor; /* global cursor information */ unsigned char keystate[256]; /* asynchronous key state */ struct key_repeat key_repeat; /* key auto-repeat */ + const desktop_shm_t *shared; /* desktop session shared memory */ };
/* user handles functions */ diff --git a/server/winstation.c b/server/winstation.c index 66fb6ad1e35..270720437b0 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -298,6 +298,12 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned list_add_tail( &winstation->desktops, &desktop->entry ); list_init( &desktop->hotkeys ); list_init( &desktop->pointers ); + + if (!(desktop->shared = alloc_shared_object())) + { + release_object( desktop ); + return NULL; + } } else { @@ -368,6 +374,7 @@ static void desktop_destroy( struct object *obj ) if (desktop->close_timeout) remove_timeout_user( desktop->close_timeout ); if (desktop->key_repeat.timeout) remove_timeout_user( desktop->key_repeat.timeout ); release_object( desktop->winstation ); + if (desktop->shared) free_shared_object( desktop->shared ); }
/* retrieve the thread desktop, checking the handle access rights */