blob: aa93d828bce7c5c5add426c1e4f7877050352956 [file] [log] [blame]
/* **********************************************************
* 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 */