jmuehlner commented on code in PR #455: URL: https://github.com/apache/guacamole-server/pull/455#discussion_r1308034049
########## src/libguac/local-lock.c: ########## @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <pthread.h> +#include <stdint.h> +#include "local-lock.h" + +/** + * The value indicating that the current thread holds neither the read or write + * locks. + */ +#define GUAC_LOCAL_LOCK_NO_LOCK 0 + +/** + * The value indicating that the current thread holds the read lock. + */ +#define GUAC_LOCAL_LOCK_READ_LOCK 1 + +/** + * The value indicating that the current thread holds the write lock. + */ +#define GUAC_LOCAL_LOCK_WRITE_LOCK 2 + +void guac_init_local_lock(guac_local_lock* lock) { + + /* Configure to allow sharing this lock with child processes */ + pthread_rwlockattr_t lock_attributes; + pthread_rwlockattr_init(&lock_attributes); + pthread_rwlockattr_setpshared(&lock_attributes, PTHREAD_PROCESS_SHARED); + + /* Initialize the rwlock */ + pthread_rwlock_init(&(lock->lock), &lock_attributes); + + /* Initialize the flags to 0, as threads won't have acquired it yet */ + pthread_key_create(&(lock->key), (void *) 0); + +} + +void guac_destroy_local_lock(guac_local_lock* lock) { + + /* Destroy the rwlock */ + pthread_rwlock_destroy(&(lock->lock)); + + /* Destroy the thread-local key */ + pthread_key_delete(lock->key); + +} + +/** + * Clean up and destroy the provided guac local lock. + * + * @param lock + * The guac local lock to be destroyed. + */ +void guac_destroy_local_lock(guac_local_lock* lock); + +/** + * Extract and return the flag indicating which lock is held, if any, from the + * provided key value. The flag is always stored in the least-significant + * nibble of the value. + * + * @param value + * The key value containing the flag. + * + * @return + * The flag indicating which lock is held, if any. + */ +static uintptr_t get_lock_flag(uintptr_t value) { + return value & 0xF; +} + +/** + * Extract and return the lock count from the provided key. This returned value + * is the difference between the number of lock and unlock requests made by the + * current thread. This count is always stored in the remaining value after the + * least-significant nibble where the flag is stored. + * + * @param value + * The key value containing the count. + * + * @return + * The difference between the number of lock and unlock requests made by + * the current thread. + */ +static uintptr_t get_lock_count(uintptr_t value) { + return value >> 4; +} + +/** + * Given a flag indicating if and how the current thread controls a lock, and + * a count of the depth of lock requests, return a value containing the flag + * in the least-significant nibble, and the count in the rest. + * + * @param flag + * A flag indiciating which lock, if any, is held by the current thread. + * + * @param count + * The depth of the lock attempt by the current thread, i.e. the number of + * lock requests minus unlock requests. + * + * @return + * A value containing the flag in the least-significant nibble, and the + * count in the rest, cast to a void* for thread-local storage. + */ +static void* get_value_from_flag_and_count( + uintptr_t flag, uintptr_t count) { + return (void*) ((flag & 0xF) | count << 4); +} + +void guac_acquire_write_lock(guac_local_lock* local_lock) { + + uintptr_t key_value = (uintptr_t) pthread_getspecific(local_lock->key); + uintptr_t flag = get_lock_flag(key_value); + uintptr_t count = get_lock_count(key_value); + + /* If the current thread already holds the write lock, increment the count */ + if (flag == GUAC_LOCAL_LOCK_WRITE_LOCK) { + pthread_setspecific(local_lock->key, get_value_from_flag_and_count( + flag, count + 1)); + + /* This thread already has the lock */ + return; + } + + /* + * The read lock must be released before the write lock can be acquired. + * This is a little odd because it may mean that a function further down + * the stack may have requested a read lock, which will get upgraded to a + * write lock by another function without the caller knowing about it. This + * shouldn't cause any issues, however. + */ + if (key_value == GUAC_LOCAL_LOCK_READ_LOCK) + pthread_rwlock_unlock(&(local_lock->lock)); + + /* Acquire the write lock */ + pthread_rwlock_wrlock(&(local_lock->lock)); + + /* Mark that the current thread has the lock, and increment the count */ + pthread_setspecific(local_lock->key, get_value_from_flag_and_count( + GUAC_LOCAL_LOCK_WRITE_LOCK, count + 1)); Review Comment: Yeah, this is probably the least of your problems if you find yourself acquiring that many locks. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
