| /* ********************************************************** |
| * Copyright (c) 2012 Google, Inc. All rights reserved. |
| * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* |
| * thread.h - thread synchronization |
| */ |
| |
| #ifndef SYNCH_H |
| #define SYNCH_H |
| |
| /*for synch */ |
| /* Given permissions */ |
| /* The order is in increasing permissiveness and the values are chosen to |
| * match up with equivalent requested states below */ |
| typedef enum { |
| THREAD_SYNCH_NONE=0, |
| |
| /* At consistent state, holding no locks, suitable for terminate, |
| * suitable for suspending */ |
| THREAD_SYNCH_NO_LOCKS=3, |
| |
| /* At consistent state, holding no locks, suitable for terminate, |
| * suitable for suspending, but cannot be transferred elsewhere: |
| * must be resumed at suspend point in order to finish an |
| * in-progress task (such as flushing or hot patch updating). |
| * Xref case 6821. |
| */ |
| THREAD_SYNCH_NO_LOCKS_NO_XFER=4, |
| |
| /* At consistent state, holding no locks, with valid mcontext |
| * (including app_errno), suitable for suspending. But, not |
| * suitable for transferring elsewhere in DR -- ok to transfer |
| * if going native, though. |
| */ |
| THREAD_SYNCH_VALID_MCONTEXT_NO_XFER=5, |
| |
| /* At consistent state, holding no locks, with valid mcontext, suitable |
| * for suspending, note that valid mcontext includes app_errno */ |
| THREAD_SYNCH_VALID_MCONTEXT=6 |
| } thread_synch_permission_t; |
| |
| /* Requested states */ |
| /* clean means that the supporting dynamorio data structures are cleaned up */ |
| /* The order is in increasingly strong requests and the values are chosen to |
| * match up with equivalent given permissions above */ |
| typedef enum { |
| THREAD_SYNCH_SUSPENDED=1, |
| THREAD_SYNCH_SUSPENDED_AND_CLEANED=2, |
| /* xref case 8747, may be best to avoid TERMINATED_AND_CLEANED where possible */ |
| THREAD_SYNCH_TERMINATED_AND_CLEANED=3, |
| |
| /* A target thread that has THREAD_SYNCH_NO_LOCKS_NO_XFER is acceptable. |
| * Xref case 6821. |
| */ |
| THREAD_SYNCH_SUSPENDED_VALID_MCONTEXT_OR_NO_XFER=4, |
| |
| THREAD_SYNCH_SUSPENDED_VALID_MCONTEXT=5 |
| } thread_synch_state_t; |
| |
| typedef enum { |
| THREAD_SYNCH_RESULT_SUCCESS = 1, |
| THREAD_SYNCH_RESULT_SUSPEND_FAILURE = 2, |
| THREAD_SYNCH_RESULT_NOT_SAFE = 3, |
| } thread_synch_result_t; |
| |
| /* option bits that affect both synch_with_all_threads() and synch_with_thread(), |
| * though the two routines interpret some options in different ways |
| */ |
| enum { |
| /* these 3 are mutually exclusive and apply to failures to suspend or |
| * to get the context of a target thread. the first two apply to |
| * both synch_with_thread() and all_threads() while the last only |
| * applies to all_threads(). |
| */ |
| THREAD_SYNCH_SUSPEND_FAILURE_ABORT = 0x00000001, |
| THREAD_SYNCH_SUSPEND_FAILURE_IGNORE = 0x00000002, |
| /* retry will cause synch_with_all_threads to keep trying until hit |
| * the loop max, so may want to combine w/ SMALL_LOOP_MAX. |
| * retry does not apply to synch_with_thread(), only to all_threads(). |
| */ |
| THREAD_SYNCH_SUSPEND_FAILURE_RETRY = 0x00000004, |
| |
| /* specifies a much smaller loop max */ |
| THREAD_SYNCH_SMALL_LOOP_MAX = 0x00000008, |
| }; |
| |
| /* convenience macros */ |
| #define THREAD_SYNCH_IS_CLEANED(desired_synch_state) ( \ |
| desired_synch_state == THREAD_SYNCH_SUSPENDED_AND_CLEANED || \ |
| desired_synch_state == THREAD_SYNCH_TERMINATED_AND_CLEANED \ |
| ) |
| #define THREAD_SYNCH_IS_TERMINATED(desired_synch_state) ( \ |
| desired_synch_state == THREAD_SYNCH_TERMINATED_AND_CLEANED \ |
| ) |
| /* desired_perm can be a thread_synch_state_t. The enums are designed to be |
| * comparable. */ |
| #define THREAD_SYNCH_SAFE(synch_perm, desired_perm) ( \ |
| synch_perm >= (thread_synch_permission_t)desired_perm \ |
| ) |
| |
| void |
| synch_init(void); |
| |
| void |
| synch_exit(void); |
| |
| void |
| synch_thread_init(dcontext_t *dcontext); |
| |
| void |
| synch_thread_exit(dcontext_t *dcontext); |
| |
| bool |
| thread_synch_state_no_xfer(dcontext_t *dcontext); |
| |
| /* Only valid while holding all_threads_synch_lock and thread_initexit_lock. Set to |
| * whether synch_with_thread was successful. |
| * Cannot be called when THREAD_SYNCH_*_AND_CLEANED was requested as the |
| * thread-local memory will be freed on success! |
| */ |
| bool |
| thread_synch_successful(thread_record_t *tr); |
| |
| /* returns a thread_synch_result_t value |
| * id - the thread you want to synch with |
| * block - whether or not should spin until synch is successful |
| * hold_initexit_lock - whether or not the caller holds the thread_initexit_lock |
| * caller_state - a given permission define from above that describes the |
| * current state of the caller (note that holding the initexit |
| * lock is ok with respect to NO_LOCK |
| * desired_state - a requested state define from above that describes the |
| * desired synchronization |
| * flags - options from THREAD_SYNCH_ bitmask values |
| * NOTE - if you hold the initexit_lock and block with greater than NONE for |
| * caller state, then initexit_lock may be released and reaquired |
| * NOTE - if any of the nt_ routines fails, it is assumed the thread no longer |
| * exists and returns true |
| * NOTE - if called directly (i.e. not through synch_with_all_threads) |
| * requires THREAD_SYNCH_IS_SAFE(caller_state, desired_state) to avoid deadlock |
| * NOTE - Requires the caller is !could_be_linking (i.e. not in an |
| * enter_couldbelinking state) |
| * NOTE - you can't call this with a thread that you've already suspended |
| */ |
| thread_synch_result_t |
| synch_with_thread(thread_id_t id, bool block, bool hold_initexit_lock, |
| thread_synch_permission_t cur_state, |
| thread_synch_state_t desired_state, |
| uint flags); |
| |
| /* held on return from synch_with_all_threads together with thread_initexit_lock*/ |
| extern mutex_t all_threads_synch_lock; |
| |
| /* desired_synch_state - a requested state define from above that describes |
| * the synchronization required |
| * threads, num_threads - must not be NULL, if !THREAD_SYNCH_IS_CLEANED(desired |
| * synch_state) then will hold a list and num of threads |
| * cur_state - a given permission from above that describes the state of the |
| * caller |
| * flags - options from THREAD_SYNCH_ bitmask values |
| * NOTE - Requires that the caller doesn't hold the thread_initexit_lock, on |
| * return caller will hold the thread_initexit_lock |
| * NOTE - Requires the caller is !could_be_linking (i.e. not in an |
| * enter_couldbelinking state) |
| * NOTE - To avoid deadlock this routine should really only be called with |
| * cur_state giving maximum permissions, (currently app_exit and detach could |
| * conflict, except our routes to app_exit go through different synch point |
| * (TermThread or TermProcess) first |
| * Caller must call end_synch_with_all_threads to clean up, unless they pass |
| * THREAD_SYNCH_SUSPEND_FAILURE_ABORT and the request fails. |
| * Note - if this is a CLIENT_INTERFACE build and the caller doesn't intend to resume all |
| * threads (say detach or process exit) then it should first call |
| * instrument_client_thread_termination() to inform the client that client-owned threads |
| * will be killed/permanently stopped. |
| */ |
| bool |
| synch_with_all_threads(thread_synch_state_t desired_synch_state, |
| thread_record_t ***threads, |
| int *num_threads, thread_synch_permission_t cur_state, |
| uint flags); |
| void |
| end_synch_with_all_threads(thread_record_t **threads, uint num_threads, bool resume); |
| void |
| resume_all_threads(thread_record_t **threads, const uint num_threads); |
| |
| /* a fast way to tell a thread if it should call check_wait_at_safe_spot |
| * if translating context would be expensive */ |
| bool |
| should_wait_at_safe_spot(dcontext_t *dcontext); |
| |
| /* checks to see if any threads are waiting to synch with this one and waits |
| * if they are |
| * cur_state - a given permission define from above that describes the current |
| * state of the caller |
| * NOTE - Requires the caller is !could_be_linking (i.e. not in an |
| * enter_couldbelinking state) |
| */ |
| void |
| check_wait_at_safe_spot(dcontext_t *dcontext, thread_synch_permission_t cur_state); |
| |
| /* use with care! normally check_wait_at_safe_spot() should be called instead */ |
| void |
| set_synch_state(dcontext_t *dcontext, thread_synch_permission_t state); |
| |
| bool |
| at_safe_spot(thread_record_t *trec, priv_mcontext_t *mc, |
| thread_synch_state_t desired_state); |
| |
| bool |
| set_synched_thread_context(thread_record_t *trec, |
| /* pass either mc or both cxt and cxt_size */ |
| priv_mcontext_t *mc, void *cxt, size_t cxt_size, |
| thread_synch_state_t desired_state |
| _IF_X64(byte *cxt_alloc) |
| _IF_WINDOWS(NTSTATUS *status/*OUT*/)); |
| |
| bool |
| translate_mcontext(thread_record_t *trec, priv_mcontext_t *mc, bool restore_memory, |
| fragment_t *f); |
| |
| /* resets a thread to start interpreting anew */ |
| void |
| translate_from_synchall_to_dispatch(thread_record_t *tr, |
| thread_synch_state_t synch_state); |
| |
| /*** exported for detach only ***/ |
| |
| bool |
| is_at_do_syscall(dcontext_t *dcontext, app_pc pc, byte *esp); |
| |
| /* adjusts the pending synch count */ |
| void |
| adjust_wait_at_safe_spot(dcontext_t *dcontext, int amt); |
| |
| #endif /* SYNCH_H */ |