| /* Get a thread-specific data pointer for a thread. |
| Copyright (C) 1999-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Contributed by Ulrich Drepper <drepper@redhat.com>, 1999. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <stdint.h> |
| #include "thread_dbP.h" |
| |
| |
| td_err_e |
| td_thr_tsd (const td_thrhandle_t *th, const thread_key_t tk, void **data) |
| { |
| td_err_e err; |
| psaddr_t tk_seq, level1, level2, seq, value; |
| void *copy; |
| uint32_t pthread_key_2ndlevel_size, idx1st, idx2nd; |
| |
| LOG ("td_thr_tsd"); |
| |
| /* Get the key entry. */ |
| err = DB_GET_VALUE (tk_seq, th->th_ta_p, __pthread_keys, tk); |
| if (err == TD_NOAPLIC) |
| return TD_BADKEY; |
| if (err != TD_OK) |
| return err; |
| |
| /* Fail if this key is not at all used. */ |
| if (((uintptr_t) tk_seq & 1) == 0) |
| return TD_BADKEY; |
| |
| /* This makes sure we have the size information on hand. */ |
| err = DB_GET_FIELD_ADDRESS (level2, th->th_ta_p, 0, pthread_key_data_level2, |
| data, 1); |
| if (err != TD_OK) |
| return err; |
| |
| /* Compute the indeces. */ |
| pthread_key_2ndlevel_size |
| = DB_DESC_NELEM (th->th_ta_p->ta_field_pthread_key_data_level2_data); |
| idx1st = tk / pthread_key_2ndlevel_size; |
| idx2nd = tk % pthread_key_2ndlevel_size; |
| |
| /* Now fetch the first level pointer. */ |
| err = DB_GET_FIELD (level1, th->th_ta_p, th->th_unique, pthread, |
| specific, idx1st); |
| if (err == TD_NOAPLIC) |
| return TD_DBERR; |
| if (err != TD_OK) |
| return err; |
| |
| /* Check the pointer to the second level array. */ |
| if (level1 == 0) |
| return TD_NOTSD; |
| |
| /* Locate the element within the second level array. */ |
| err = DB_GET_FIELD_ADDRESS (level2, th->th_ta_p, |
| level1, pthread_key_data_level2, data, idx2nd); |
| if (err == TD_NOAPLIC) |
| return TD_DBERR; |
| if (err != TD_OK) |
| return err; |
| |
| /* Now copy in that whole structure. */ |
| err = DB_GET_STRUCT (copy, th->th_ta_p, level2, pthread_key_data); |
| if (err != TD_OK) |
| return err; |
| |
| /* Check whether the data is valid. */ |
| err = DB_GET_FIELD_LOCAL (seq, th->th_ta_p, copy, pthread_key_data, seq, 0); |
| if (err != TD_OK) |
| return err; |
| if (seq != tk_seq) |
| return TD_NOTSD; |
| |
| /* Finally, fetch the value. */ |
| err = DB_GET_FIELD_LOCAL (value, th->th_ta_p, copy, pthread_key_data, |
| data, 0); |
| if (err == TD_OK) |
| *data = value; |
| |
| return err; |
| } |