Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
6b3654f9
Commit
6b3654f9
authored
Mar 25, 2013
by
Michael Widenius
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge
parents
22f91edd
2c40fb56
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
251 additions
and
246 deletions
+251
-246
include/violite.h
include/violite.h
+1
-1
mysql-test/suite/perfschema/r/all_instances.result
mysql-test/suite/perfschema/r/all_instances.result
+1
-0
mysql-test/suite/perfschema/r/func_mutex.result
mysql-test/suite/perfschema/r/func_mutex.result
+13
-31
mysql-test/suite/perfschema/t/func_mutex.test
mysql-test/suite/perfschema/t/func_mutex.test
+9
-9
mysys/my_gethwaddr.c
mysys/my_gethwaddr.c
+7
-3
mysys/my_rnd.c
mysys/my_rnd.c
+12
-3
sql/event_scheduler.cc
sql/event_scheduler.cc
+6
-20
sql/mysqld.cc
sql/mysqld.cc
+125
-90
sql/mysqld.h
sql/mysqld.h
+18
-29
sql/sql_acl.cc
sql/sql_acl.cc
+24
-13
sql/sql_class.cc
sql/sql_class.cc
+8
-33
sql/sql_class.h
sql/sql_class.h
+4
-1
sql/sql_insert.cc
sql/sql_insert.cc
+4
-4
sql/sql_parse.cc
sql/sql_parse.cc
+16
-4
vio/vio.c
vio/vio.c
+3
-5
No files found.
include/violite.h
View file @
6b3654f9
...
...
@@ -216,7 +216,7 @@ struct st_vio
struct
sockaddr_storage
remote
;
/* Remote internet address */
int
addrLen
;
/* Length of remote address */
enum
enum_vio_type
type
;
/* Type of connection */
c
har
desc
[
30
];
/* String description */
c
onst
char
*
desc
;
/* String description */
char
*
read_buffer
;
/* buffer for vio_read_buff */
char
*
read_pos
;
/* start of unfetched data in the
read buffer */
...
...
mysql-test/suite/perfschema/r/all_instances.result
View file @
6b3654f9
...
...
@@ -64,6 +64,7 @@ wait/synch/mutex/sql/LOCK_server_started
wait/synch/mutex/sql/LOCK_slave_list
wait/synch/mutex/sql/LOCK_stats
wait/synch/mutex/sql/LOCK_status
wait/synch/mutex/sql/LOCK_thread_cache
wait/synch/mutex/sql/LOCK_thread_count
wait/synch/mutex/sql/LOCK_user_conn
wait/synch/mutex/sql/LOCK_user_locks
...
...
mysql-test/suite/perfschema/r/func_mutex.result
View file @
6b3654f9
...
...
@@ -70,52 +70,34 @@ Success
TRUNCATE TABLE performance_schema.events_waits_history_long;
TRUNCATE TABLE performance_schema.events_waits_history;
TRUNCATE TABLE performance_schema.events_waits_current;
SELECT * FROM t1 WHERE id = 1;
id b
1 initial value
show variables like "%not_found%";
Variable_name Value
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/rwlock/sql/LOCK_grant'));
SELECT * FROM t1;
id b
1 initial value
2 initial value
3 initial value
4 initial value
5 initial value
6 initial value
7 initial value
8 initial value
WHERE (EVENT_NAME = 'wait/synch/rwlock/sql/LOCK_system_variables_hash'));
show variables like "%not_found%";
Variable_name Value
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/rwlock/sql/LOCK_
grant
'));
WHERE (EVENT_NAME = 'wait/synch/rwlock/sql/LOCK_
system_variables_hash
'));
SELECT IF((@after_count - @before_count) > 0, 'Success', 'Failure') test_fm1_rw_timed;
test_fm1_rw_timed
Success
UPDATE performance_schema.setup_instruments SET enabled = 'NO'
WHERE NAME = 'wait/synch/rwlock/sql/LOCK_
grant
';
WHERE NAME = 'wait/synch/rwlock/sql/LOCK_
system_variables_hash
';
TRUNCATE TABLE performance_schema.events_waits_history_long;
TRUNCATE TABLE performance_schema.events_waits_history;
TRUNCATE TABLE performance_schema.events_waits_current;
SELECT * FROM t1 WHERE id = 1;
id b
1 initial value
show variables like "%not_found%";
Variable_name Value
SET @before_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = 'wait/synch/rwlock/sql/LOCK_grant'));
SELECT * FROM t1;
id b
1 initial value
2 initial value
3 initial value
4 initial value
5 initial value
6 initial value
7 initial value
8 initial value
WHERE (EVENT_NAME = 'wait/synch/rwlock/sql/LOCK_system_variables_hash'));
show variables like "%not_found%";
Variable_name Value
SET @after_count = (SELECT SUM(TIMER_WAIT)
FROM performance_schema.events_waits_history_long
WHERE (EVENT_NAME = '
wait/synch/rwlock/sql/LOCK_grant
'));
WHERE (EVENT_NAME = '
LOCK_system_variables_hash
'));
SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_rw_timed;
test_fm2_rw_timed
Success
...
...
mysql-test/suite/perfschema/t/func_mutex.test
View file @
6b3654f9
...
...
@@ -87,38 +87,38 @@ TRUNCATE TABLE performance_schema.events_waits_history_long;
TRUNCATE
TABLE
performance_schema
.
events_waits_history
;
TRUNCATE
TABLE
performance_schema
.
events_waits_current
;
SELECT
*
FROM
t1
WHERE
id
=
1
;
show
variables
like
"%not_found%"
;
SET
@
before_count
=
(
SELECT
SUM
(
TIMER_WAIT
)
FROM
performance_schema
.
events_waits_history_long
WHERE
(
EVENT_NAME
=
'wait/synch/rwlock/sql/LOCK_
grant
'
));
WHERE
(
EVENT_NAME
=
'wait/synch/rwlock/sql/LOCK_
system_variables_hash
'
));
SELECT
*
FROM
t1
;
show
variables
like
"%not_found%"
;
SET
@
after_count
=
(
SELECT
SUM
(
TIMER_WAIT
)
FROM
performance_schema
.
events_waits_history_long
WHERE
(
EVENT_NAME
=
'wait/synch/rwlock/sql/LOCK_
grant
'
));
WHERE
(
EVENT_NAME
=
'wait/synch/rwlock/sql/LOCK_
system_variables_hash
'
));
SELECT
IF
((
@
after_count
-
@
before_count
)
>
0
,
'Success'
,
'Failure'
)
test_fm1_rw_timed
;
UPDATE
performance_schema
.
setup_instruments
SET
enabled
=
'NO'
WHERE
NAME
=
'wait/synch/rwlock/sql/LOCK_
grant
'
;
WHERE
NAME
=
'wait/synch/rwlock/sql/LOCK_
system_variables_hash
'
;
TRUNCATE
TABLE
performance_schema
.
events_waits_history_long
;
TRUNCATE
TABLE
performance_schema
.
events_waits_history
;
TRUNCATE
TABLE
performance_schema
.
events_waits_current
;
SELECT
*
FROM
t1
WHERE
id
=
1
;
show
variables
like
"%not_found%"
;
SET
@
before_count
=
(
SELECT
SUM
(
TIMER_WAIT
)
FROM
performance_schema
.
events_waits_history_long
WHERE
(
EVENT_NAME
=
'wait/synch/rwlock/sql/LOCK_
grant
'
));
WHERE
(
EVENT_NAME
=
'wait/synch/rwlock/sql/LOCK_
system_variables_hash
'
));
SELECT
*
FROM
t1
;
show
variables
like
"%not_found%"
;
SET
@
after_count
=
(
SELECT
SUM
(
TIMER_WAIT
)
FROM
performance_schema
.
events_waits_history_long
WHERE
(
EVENT_NAME
=
'
wait/synch/rwlock/sql/LOCK_grant
'
));
WHERE
(
EVENT_NAME
=
'
LOCK_system_variables_hash
'
));
SELECT
IF
((
COALESCE
(
@
after_count
,
0
)
-
COALESCE
(
@
before_count
,
0
))
=
0
,
'Success'
,
'Failure'
)
test_fm2_rw_timed
;
...
...
mysys/my_gethwaddr.c
View file @
6b3654f9
...
...
@@ -87,13 +87,17 @@ my_bool my_gethwaddr(uchar *to)
int
fd
,
res
=
1
;
struct
ifreq
ifr
[
32
];
struct
ifconf
ifc
;
DBUG_ENTER
(
"my_gethwaddr"
);
ifc
.
ifc_req
=
ifr
;
ifc
.
ifc_len
=
sizeof
(
ifr
);
fd
=
socket
(
AF_INET
,
SOCK_DGRAM
,
0
);
if
(
fd
<
0
)
{
DBUG_PRINT
(
"error"
,
(
"socket() call failed with %d"
,
errno
));
goto
err
;
}
if
(
ioctl
(
fd
,
SIOCGIFCONF
,
(
char
*
)
&
ifc
)
>=
0
)
{
...
...
@@ -106,8 +110,8 @@ my_bool my_gethwaddr(uchar *to)
ETHER_ADDR_LEN
);
#else
/*
A bug in OpenSolaris used to prevent non-root from getting a mac
address:
{no url. Oracle killed the old OpenSolaris bug database}
A bug in OpenSolaris used to prevent non-root from getting a mac
address:
{no url. Oracle killed the old OpenSolaris bug database}
Thus, we'll use an alternative method and extract the address from the
arp table.
...
...
@@ -124,7 +128,7 @@ my_bool my_gethwaddr(uchar *to)
close
(
fd
);
err:
return
res
;
DBUG_RETURN
(
res
)
;
}
#elif defined(_WIN32)
...
...
mysys/my_rnd.c
View file @
6b3654f9
...
...
@@ -45,11 +45,20 @@ void my_rnd_init(struct my_rnd_struct *rand_st, ulong seed1, ulong seed2)
RETURN VALUE
generated pseudo random number
NOTE:
This is codes so that it can be called by two threads at the same time
with minimum impact.
(As the number is supposed to be random, it doesn't matter much if
rand->seed1 or rand->seed2 are updated with slightly wrong numbers or
if two threads gets the same number.
*/
double
my_rnd
(
struct
my_rnd_struct
*
rand_st
)
{
rand_st
->
seed1
=
(
rand_st
->
seed1
*
3
+
rand_st
->
seed2
)
%
rand_st
->
max_value
;
rand_st
->
seed2
=
(
rand_st
->
seed1
+
rand_st
->
seed2
+
33
)
%
rand_st
->
max_value
;
return
(((
double
)
rand_st
->
seed1
)
/
rand_st
->
max_value_dbl
);
unsigned
long
seed1
;
seed1
=
(
rand_st
->
seed1
*
3
+
rand_st
->
seed2
)
%
rand_st
->
max_value
;
rand_st
->
seed2
=
(
seed1
+
rand_st
->
seed2
+
33
)
%
rand_st
->
max_value
;
rand_st
->
seed1
=
seed1
;
return
(((
double
)
seed1
)
/
rand_st
->
max_value_dbl
);
}
sql/event_scheduler.cc
View file @
6b3654f9
...
...
@@ -132,11 +132,11 @@ post_init_event_thread(THD *thd)
return
TRUE
;
}
thread_safe_increment32
(
&
thread_count
,
&
thread_count_lock
);
mysql_mutex_lock
(
&
LOCK_thread_count
);
threads
.
append
(
thd
);
thread_count
++
;
inc_thread_running
();
mysql_mutex_unlock
(
&
LOCK_thread_count
);
inc_thread_running
();
return
FALSE
;
}
...
...
@@ -154,12 +154,8 @@ deinit_event_thread(THD *thd)
{
thd
->
proc_info
=
"Clearing"
;
DBUG_PRINT
(
"exit"
,
(
"Event thread finishing"
));
mysql_mutex_lock
(
&
LOCK_thread_count
);
thread_count
--
;
dec_thread_running
();
delete
thd
;
mysql_cond_broadcast
(
&
COND_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
delete_running_thd
(
thd
);
}
...
...
@@ -436,12 +432,7 @@ Event_scheduler::start()
ret
=
TRUE
;
new_thd
->
proc_info
=
"Clearing"
;
mysql_mutex_lock
(
&
LOCK_thread_count
);
thread_count
--
;
dec_thread_running
();
delete
new_thd
;
mysql_cond_broadcast
(
&
COND_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
delete_running_thd
(
new_thd
);
}
end:
UNLOCK_DATA
();
...
...
@@ -570,12 +561,7 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
if
(
new_thd
)
{
new_thd
->
proc_info
=
"Clearing"
;
mysql_mutex_lock
(
&
LOCK_thread_count
);
thread_count
--
;
dec_thread_running
();
delete
new_thd
;
mysql_cond_broadcast
(
&
COND_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
delete_running_thd
(
new_thd
);
}
delete
event_name
;
DBUG_RETURN
(
TRUE
);
...
...
sql/mysqld.cc
View file @
6b3654f9
...
...
@@ -465,7 +465,7 @@ ulong delay_key_write_options;
uint
protocol_version
;
uint
lower_case_table_names
;
ulong
tc_heuristic_recover
=
0
;
uint
volatile
thread_count
;
int32
thread_count
;
int32
thread_running
;
ulong
thread_created
;
ulong
back_log
,
connect_timeout
,
concurrency
,
server_id
;
...
...
@@ -489,6 +489,7 @@ ulong executed_events=0;
query_id_t
global_query_id
;
my_atomic_rwlock_t
global_query_id_lock
;
my_atomic_rwlock_t
thread_running_lock
;
my_atomic_rwlock_t
thread_count_lock
;
my_atomic_rwlock_t
statistics_lock
;
ulong
aborted_threads
,
aborted_connects
;
ulong
delayed_insert_timeout
,
delayed_insert_limit
,
delayed_queue_size
;
...
...
@@ -663,7 +664,7 @@ SHOW_COMP_OPTION have_openssl;
pthread_key
(
MEM_ROOT
**
,
THR_MALLOC
);
pthread_key
(
THD
*
,
THR_THD
);
mysql_mutex_t
LOCK_thread_count
;
mysql_mutex_t
LOCK_thread_count
,
LOCK_thread_cache
;
mysql_mutex_t
LOCK_status
,
LOCK_error_log
,
LOCK_short_uuid_generator
,
LOCK_delayed_insert
,
LOCK_delayed_status
,
LOCK_delayed_create
,
...
...
@@ -761,7 +762,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_relay_log_info_log_space_lock
,
key_relay_log_info_run_lock
,
key_relay_log_info_sleep_lock
,
key_structure_guard_mutex
,
key_TABLE_SHARE_LOCK_ha_data
,
key_LOCK_error_messages
,
key_LOG_INFO_lock
,
key_LOCK_thread_count
,
key_LOCK_error_messages
,
key_LOG_INFO_lock
,
key_LOCK_thread_count
,
key_LOCK_thread_cache
,
key_PARTITION_LOCK_auto_inc
;
PSI_mutex_key
key_RELAYLOG_LOCK_index
;
...
...
@@ -832,6 +834,7 @@ static PSI_mutex_info all_server_mutexes[]=
{
&
key_LOCK_commit_ordered
,
"LOCK_commit_ordered"
,
PSI_FLAG_GLOBAL
},
{
&
key_LOG_INFO_lock
,
"LOG_INFO::lock"
,
0
},
{
&
key_LOCK_thread_count
,
"LOCK_thread_count"
,
PSI_FLAG_GLOBAL
},
{
&
key_LOCK_thread_cache
,
"LOCK_thread_cache"
,
PSI_FLAG_GLOBAL
},
{
&
key_PARTITION_LOCK_auto_inc
,
"HA_DATA_PARTITION::LOCK_auto_inc"
,
0
}
};
...
...
@@ -1472,7 +1475,7 @@ static void close_connections(void)
end_slave
();
/* Give threads time to die. */
for
(
int
i
=
0
;
thread_count
&&
i
<
100
;
i
++
)
for
(
int
i
=
0
;
*
(
volatile
int32
*
)
&
thread_count
&&
i
<
100
;
i
++
)
my_sleep
(
20000
);
/*
...
...
@@ -1886,6 +1889,7 @@ void clean_up(bool print_message)
sys_var_end
();
my_atomic_rwlock_destroy
(
&
global_query_id_lock
);
my_atomic_rwlock_destroy
(
&
thread_running_lock
);
my_atomic_rwlock_destroy
(
&
thread_count_lock
);
my_atomic_rwlock_destroy
(
&
statistics_lock
);
mysql_mutex_lock
(
&
LOCK_thread_count
);
DBUG_PRINT
(
"quit"
,
(
"got thread count lock"
));
...
...
@@ -1929,6 +1933,7 @@ static void clean_up_mutexes()
DBUG_ENTER
(
"clean_up_mutexes"
);
mysql_rwlock_destroy
(
&
LOCK_grant
);
mysql_mutex_destroy
(
&
LOCK_thread_count
);
mysql_mutex_destroy
(
&
LOCK_thread_cache
);
mysql_mutex_destroy
(
&
LOCK_status
);
mysql_mutex_destroy
(
&
LOCK_delayed_insert
);
mysql_mutex_destroy
(
&
LOCK_delayed_status
);
...
...
@@ -2455,6 +2460,28 @@ void dec_connection_count(THD *thd)
}
/*
Delete THD and decrement thread counters, including thread_running
*/
void
delete_running_thd
(
THD
*
thd
)
{
mysql_mutex_lock
(
&
LOCK_thread_count
);
thd
->
unlink
();
mysql_mutex_unlock
(
&
LOCK_thread_count
);
delete
thd
;
dec_thread_running
();
thread_safe_decrement32
(
&
thread_count
,
&
thread_count_lock
);
if
(
!
thread_count
)
{
mysql_mutex_lock
(
&
LOCK_thread_count
);
mysql_cond_broadcast
(
&
COND_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
}
}
/*
Unlink thd from global list of available connections and free thd
...
...
@@ -2479,7 +2506,6 @@ void unlink_thd(THD *thd)
mysql_mutex_unlock
(
&
LOCK_status
);
mysql_mutex_lock
(
&
LOCK_thread_count
);
thread_count
--
;
thd
->
unlink
();
/*
Used by binlog_reset_master. It would be cleaner to use
...
...
@@ -2487,13 +2513,11 @@ void unlink_thd(THD *thd)
sync feature has been shut down at this point.
*/
DBUG_EXECUTE_IF
(
"sleep_after_lock_thread_count_before_delete_thd"
,
sleep
(
5
););
/*
We must delete thd inside the lock to ensure that we don't start cleanup
before THD is deleted
*/
delete
thd
;
mysql_mutex_unlock
(
&
LOCK_thread_count
);
delete
thd
;
thread_safe_decrement32
(
&
thread_count
,
&
thread_count_lock
);
DBUG_VOID_RETURN
;
}
...
...
@@ -2505,7 +2529,7 @@ void unlink_thd(THD *thd)
cache_thread()
NOTES
LOCK_thread_c
ount has to be locked
LOCK_thread_c
ache is used to protect the cache variables
RETURN
0 Thread was not put in cache
...
...
@@ -2516,7 +2540,9 @@ void unlink_thd(THD *thd)
static
bool
cache_thread
()
{
mysql_mutex_assert_owner
(
&
LOCK_thread_count
);
DBUG_ENTER
(
"cache_thread"
);
mysql_mutex_lock
(
&
LOCK_thread_cache
);
if
(
cached_thread_count
<
thread_cache_size
&&
!
abort_loop
&&
!
kill_cached_threads
)
{
...
...
@@ -2534,7 +2560,7 @@ static bool cache_thread()
#endif
while
(
!
abort_loop
&&
!
wake_thread
&&
!
kill_cached_threads
)
mysql_cond_wait
(
&
COND_thread_cache
,
&
LOCK_thread_c
ount
);
mysql_cond_wait
(
&
COND_thread_cache
,
&
LOCK_thread_c
ache
);
cached_thread_count
--
;
if
(
kill_cached_threads
)
mysql_cond_signal
(
&
COND_flush_thread_cache
);
...
...
@@ -2543,6 +2569,8 @@ static bool cache_thread()
THD
*
thd
;
wake_thread
--
;
thd
=
thread_cache
.
get
();
mysql_mutex_unlock
(
&
LOCK_thread_cache
);
thd
->
thread_stack
=
(
char
*
)
&
thd
;
// For store_globals
(
void
)
thd
->
store_globals
();
...
...
@@ -2568,11 +2596,16 @@ static bool cache_thread()
thd
->
mysys_var
->
abort
=
0
;
thd
->
thr_create_utime
=
microsecond_interval_timer
();
thd
->
start_utime
=
thd
->
thr_create_utime
;
/* Link thd into list of all active threads (THD's) */
mysql_mutex_lock
(
&
LOCK_thread_count
);
threads
.
append
(
thd
);
return
(
1
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
DBUG_RETURN
(
1
);
}
}
return
(
0
);
mysql_mutex_unlock
(
&
LOCK_thread_cache
);
DBUG_RETURN
(
0
);
}
...
...
@@ -2601,19 +2634,22 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
unlink_thd
(
thd
);
/* Mark that current_thd is not valid anymore */
set_current_thd
(
0
);
if
(
put_in_cache
)
if
(
put_in_cache
&&
cache_thread
())
DBUG_RETURN
(
0
);
// Thread is reused
/*
It's safe to check for thread_count outside of the mutex
as we are only interested to see if it was counted to 0 by the
above unlink_thd() call. We should only signal COND_thread_count if
thread_count is likely to be 0. (false positives are ok)
*/
if
(
!
thread_count
)
{
mysql_mutex_lock
(
&
LOCK_thread_count
);
put_in_cache
=
cache_thread
();
DBUG_PRINT
(
"signal"
,
(
"Broadcasting COND_thread_count"
));
mysql_cond_broadcast
(
&
COND_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
if
(
put_in_cache
)
DBUG_RETURN
(
0
);
// Thread is reused
}
/* It's safe to broadcast outside a lock (COND... is not deleted here) */
DBUG_PRINT
(
"signal"
,
(
"Broadcasting COND_thread_count"
));
mysql_cond_broadcast
(
&
COND_thread_count
);
DBUG_LEAVE
;
// Must match DBUG_ENTER()
my_thread_end
();
...
...
@@ -2624,15 +2660,17 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
void
flush_thread_cache
()
{
mysql_mutex_lock
(
&
LOCK_thread_count
);
DBUG_ENTER
(
"flush_thread_cache"
);
mysql_mutex_lock
(
&
LOCK_thread_cache
);
kill_cached_threads
++
;
while
(
cached_thread_count
)
{
mysql_cond_broadcast
(
&
COND_thread_cache
);
mysql_cond_wait
(
&
COND_flush_thread_cache
,
&
LOCK_thread_c
ount
);
mysql_cond_wait
(
&
COND_flush_thread_cache
,
&
LOCK_thread_c
ache
);
}
kill_cached_threads
--
;
mysql_mutex_unlock
(
&
LOCK_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_cache
);
DBUG_VOID_RETURN
;
}
...
...
@@ -3973,6 +4011,7 @@ static int init_thread_environment()
{
DBUG_ENTER
(
"init_thread_environment"
);
mysql_mutex_init
(
key_LOCK_thread_count
,
&
LOCK_thread_count
,
MY_MUTEX_INIT_FAST
);
mysql_mutex_init
(
key_LOCK_thread_cache
,
&
LOCK_thread_cache
,
MY_MUTEX_INIT_FAST
);
mysql_mutex_init
(
key_LOCK_status
,
&
LOCK_status
,
MY_MUTEX_INIT_FAST
);
mysql_mutex_init
(
key_LOCK_delayed_insert
,
&
LOCK_delayed_insert
,
MY_MUTEX_INIT_FAST
);
...
...
@@ -5363,7 +5402,7 @@ static void bootstrap(MYSQL_FILE *file)
thd
->
max_client_packet_length
=
thd
->
net
.
max_packet
;
thd
->
security_ctx
->
master_access
=
~
(
ulong
)
0
;
thd
->
thread_id
=
thd
->
variables
.
pseudo_thread_id
=
thread_id
++
;
thread_count
++
;
thread_count
++
;
// Safe as only one thread running
in_bootstrap
=
TRUE
;
bootstrap_file
=
file
;
...
...
@@ -5444,54 +5483,69 @@ void handle_connection_in_main_thread(THD *thd)
void
create_thread_to_handle_connection
(
THD
*
thd
)
{
DBUG_ENTER
(
"create_thread_to_handle_connection"
);
mysql_mutex_assert_owner
(
&
LOCK_thread_count
);
/* Check if we can get thread from the cache */
if
(
cached_thread_count
>
wake_thread
)
{
/* Get thread from cache */
thread_cache
.
push_back
(
thd
);
wake_thread
++
;
mysql_cond_signal
(
&
COND_thread_cache
);
}
else
{
char
error_message_buff
[
MYSQL_ERRMSG_SIZE
];
/* Create new thread to handle connection */
int
error
;
thread_created
++
;
threads
.
append
(
thd
);
DBUG_PRINT
(
"info"
,((
"creating thread %lu"
),
thd
->
thread_id
));
thd
->
prior_thr_create_utime
=
microsecond_interval_timer
();
if
((
error
=
mysql_thread_create
(
key_thread_one_connection
,
&
thd
->
real_id
,
&
connection_attrib
,
handle_one_connection
,
(
void
*
)
thd
)))
mysql_mutex_lock
(
&
LOCK_thread_cache
);
/* Recheck condition when we have the lock */
if
(
cached_thread_count
>
wake_thread
)
{
/* purecov: begin inspected */
DBUG_PRINT
(
"error"
,
(
"Can't create thread to handle request (error %d)"
,
error
));
thread_count
--
;
thd
->
killed
=
KILL_CONNECTION
;
// Safety
mysql_mutex_unlock
(
&
LOCK_thread_count
);
/* Get thread from cache */
thread_cache
.
push_back
(
thd
);
wake_thread
++
;
mysql_cond_signal
(
&
COND_thread_cache
);
mysql_mutex_unlock
(
&
LOCK_thread_cache
);
DBUG_PRINT
(
"info"
,(
"Thread created"
));
DBUG_VOID_RETURN
;
}
mysql_mutex_unlock
(
&
LOCK_thread_cache
);
}
char
error_message_buff
[
MYSQL_ERRMSG_SIZE
];
/* Create new thread to handle connection */
int
error
;
thread_created
++
;
threads
.
append
(
thd
);
DBUG_PRINT
(
"info"
,((
"creating thread %lu"
),
thd
->
thread_id
));
thd
->
prior_thr_create_utime
=
microsecond_interval_timer
();
if
((
error
=
mysql_thread_create
(
key_thread_one_connection
,
&
thd
->
real_id
,
&
connection_attrib
,
handle_one_connection
,
(
void
*
)
thd
)))
{
/* purecov: begin inspected */
DBUG_PRINT
(
"error"
,
(
"Can't create thread to handle request (error %d)"
,
error
));
thd
->
killed
=
KILL_CONNECTION
;
// Safety
mysql_mutex_unlock
(
&
LOCK_thread_count
);
mysql_mutex_lock
(
&
LOCK_connection_count
);
(
*
thd
->
scheduler
->
connection_count
)
--
;
mysql_mutex_unlock
(
&
LOCK_connection_count
);
mysql_mutex_lock
(
&
LOCK_connection_count
);
(
*
thd
->
scheduler
->
connection_count
)
--
;
mysql_mutex_unlock
(
&
LOCK_connection_count
);
statistic_increment
(
aborted_connects
,
&
LOCK_status
);
/* Can't use my_error() since store_globals has not been called. */
my_snprintf
(
error_message_buff
,
sizeof
(
error_message_buff
),
ER_THD
(
thd
,
ER_CANT_CREATE_THREAD
),
error
);
net_send_error
(
thd
,
ER_CANT_CREATE_THREAD
,
error_message_buff
,
NULL
);
close_connection
(
thd
,
ER_OUT_OF_RESOURCES
);
mysql_mutex_lock
(
&
LOCK_thread_count
);
delete
thd
;
mysql_mutex_unlock
(
&
LOCK_thread_count
);
return
;
/* purecov: end */
}
statistic_increment
(
aborted_connects
,
&
LOCK_status
);
/* Can't use my_error() since store_globals has not been called. */
my_snprintf
(
error_message_buff
,
sizeof
(
error_message_buff
),
ER_THD
(
thd
,
ER_CANT_CREATE_THREAD
),
error
);
net_send_error
(
thd
,
ER_CANT_CREATE_THREAD
,
error_message_buff
,
NULL
);
close_connection
(
thd
,
ER_OUT_OF_RESOURCES
);
mysql_mutex_lock
(
&
LOCK_thread_count
);
thd
->
unlink
();
mysql_mutex_unlock
(
&
LOCK_thread_count
);
delete
thd
;
thread_safe_decrement32
(
&
thread_count
,
&
thread_count_lock
);
return
;
/* purecov: end */
}
mysql_mutex_unlock
(
&
LOCK_thread_count
);
DBUG_PRINT
(
"info"
,(
"Thread created"
));
DBUG_VOID_RETURN
;
}
...
...
@@ -5538,10 +5592,10 @@ static void create_new_thread(THD *thd)
mysql_mutex_unlock
(
&
LOCK_connection_count
);
/* Start a new thread to handle connection. */
thread_safe_increment32
(
&
thread_count
,
&
thread_count_lock
);
/* Start a new thread to handle connection. */
mysql_mutex_lock
(
&
LOCK_thread_count
);
/*
The initialization of thread_id is done in create_embedded_thd() for
the embedded library.
...
...
@@ -5549,8 +5603,6 @@ static void create_new_thread(THD *thd)
*/
thd
->
thread_id
=
thd
->
variables
.
pseudo_thread_id
=
thread_id
++
;
thread_count
++
;
MYSQL_CALLBACK
(
thd
->
scheduler
,
add_connection
,
(
thd
));
DBUG_VOID_RETURN
;
...
...
@@ -5758,20 +5810,6 @@ void handle_connections_sockets()
}
#endif
/* HAVE_LIBWRAP */
{
size_socket
dummyLen
;
struct
sockaddr_storage
dummy
;
dummyLen
=
sizeof
(
dummy
);
if
(
getsockname
(
new_sock
,(
struct
sockaddr
*
)
&
dummy
,
(
SOCKET_SIZE_TYPE
*
)
&
dummyLen
)
<
0
)
{
sql_perror
(
"Error on new connection socket"
);
(
void
)
mysql_socket_shutdown
(
new_sock
,
SHUT_RDWR
);
(
void
)
closesocket
(
new_sock
);
continue
;
}
}
/*
** Don't allow too many connections
*/
...
...
@@ -7402,6 +7440,7 @@ static int mysql_init_variables(void)
global_query_id
=
thread_id
=
1L
;
my_atomic_rwlock_init
(
&
global_query_id_lock
);
my_atomic_rwlock_init
(
&
thread_running_lock
);
my_atomic_rwlock_init
(
&
thread_count_lock
);
my_atomic_rwlock_init
(
&
statistics_lock
);
strmov
(
server_version
,
MYSQL_SERVER_VERSION
);
threads
.
empty
();
...
...
@@ -8461,12 +8500,8 @@ void refresh_status(THD *thd)
/*
Set max_used_connections to the number of currently open
connections. Lock LOCK_thread_count out of LOCK_status to avoid
deadlocks. Status reset becomes not atomic, but status data is
not exact anyway.
connections. This is not perfect, but status data is not exact anyway.
*/
mysql_mutex_lock
(
&
LOCK_thread_count
);
max_used_connections
=
thread_count
-
delayed_insert_threads
;
mysql_mutex_unlock
(
&
LOCK_thread_count
);
}
sql/mysqld.h
View file @
6b3654f9
...
...
@@ -56,6 +56,7 @@ void kill_mysql(void);
void
close_connection
(
THD
*
thd
,
uint
sql_errno
=
0
);
void
handle_connection_in_main_thread
(
THD
*
thd
);
void
create_thread_to_handle_connection
(
THD
*
thd
);
void
delete_running_thd
(
THD
*
thd
);
void
unlink_thd
(
THD
*
thd
);
bool
one_thread_per_connection_end
(
THD
*
thd
,
bool
put_in_cache
);
void
flush_thread_cache
();
...
...
@@ -89,7 +90,6 @@ extern bool opt_ignore_builtin_innodb;
extern
my_bool
opt_character_set_client_handshake
;
extern
bool
volatile
abort_loop
;
extern
bool
in_bootstrap
;
extern
uint
volatile
thread_count
;
extern
uint
connection_count
;
extern
my_bool
opt_safe_user_create
;
extern
my_bool
opt_safe_show_db
,
opt_local_infile
,
opt_myisam_use_mmap
;
...
...
@@ -352,7 +352,8 @@ extern mysql_rwlock_t LOCK_system_variables_hash;
extern
mysql_cond_t
COND_thread_count
;
extern
mysql_cond_t
COND_manager
;
extern
int32
thread_running
;
extern
my_atomic_rwlock_t
thread_running_lock
;
extern
int32
thread_count
;
extern
my_atomic_rwlock_t
thread_running_lock
,
thread_count_lock
;
extern
char
*
opt_ssl_ca
,
*
opt_ssl_capath
,
*
opt_ssl_cert
,
*
opt_ssl_cipher
,
*
opt_ssl_key
;
...
...
@@ -449,7 +450,7 @@ inline query_id_t next_query_id()
my_atomic_rwlock_wrlock
(
&
global_query_id_lock
);
id
=
my_atomic_add64
(
&
global_query_id
,
1
);
my_atomic_rwlock_wrunlock
(
&
global_query_id_lock
);
return
(
id
+
1
);
return
(
id
);
}
inline
query_id_t
get_query_id
()
...
...
@@ -479,42 +480,30 @@ inline void table_case_convert(char * name, uint length)
name
,
length
,
name
,
length
);
}
inline
ulong
sql_rnd_with_mutex
(
)
inline
void
thread_safe_increment32
(
int32
*
value
,
my_atomic_rwlock_t
*
lock
)
{
mysql_mutex_lock
(
&
LOCK_thread_count
);
ulong
tmp
=
(
ulong
)
(
my_rnd
(
&
sql_rand
)
*
0xffffffff
);
/* make all bits random */
mysql_mutex_unlock
(
&
LOCK_thread_count
);
return
tmp
;
my_atomic_rwlock_wrlock
(
lock
);
(
void
)
my_atomic_add32
(
value
,
1
);
my_atomic_rwlock_wrunlock
(
lock
);
}
inline
int32
inc_thread_running
()
inline
void
thread_safe_decrement32
(
int32
*
value
,
my_atomic_rwlock_t
*
lock
)
{
int32
num_thread_running
;
my_atomic_rwlock_wrlock
(
&
thread_running_lock
);
num_thread_running
=
my_atomic_add32
(
&
thread_running
,
1
);
my_atomic_rwlock_wrunlock
(
&
thread_running_lock
);
return
(
num_thread_running
+
1
);
my_atomic_rwlock_wrlock
(
lock
);
(
void
)
my_atomic_add32
(
value
,
-
1
);
my_atomic_rwlock_wrunlock
(
lock
);
}
inline
int32
de
c_thread_running
()
inline
void
in
c_thread_running
()
{
int32
num_thread_running
;
my_atomic_rwlock_wrlock
(
&
thread_running_lock
);
num_thread_running
=
my_atomic_add32
(
&
thread_running
,
-
1
);
my_atomic_rwlock_wrunlock
(
&
thread_running_lock
);
return
(
num_thread_running
-
1
);
thread_safe_increment32
(
&
thread_running
,
&
thread_running_lock
);
}
inline
int32
get
_thread_running
()
inline
void
dec
_thread_running
()
{
int32
num_thread_running
;
my_atomic_rwlock_wrlock
(
&
thread_running_lock
);
num_thread_running
=
my_atomic_load32
(
&
thread_running
);
my_atomic_rwlock_wrunlock
(
&
thread_running_lock
);
return
num_thread_running
;
thread_safe_decrement32
(
&
thread_running
,
&
thread_running_lock
);
}
void
set_server_version
(
void
);
...
...
sql/sql_acl.cc
View file @
6b3654f9
...
...
@@ -4530,12 +4530,16 @@ my_bool grant_reload(THD *thd)
@see check_access
@see check_table_access
@note This functions assumes that either number of tables to be inspected
@note
This functions assumes that either number of tables to be inspected
by it is limited explicitly (i.e. is is not UINT_MAX) or table list
used and thd->lex->query_tables_own_last value correspond to each
other (the latter should be either 0 or point to next_global member
of one of elements of this table list).
We delay locking of LOCK_grant until we really need it as we assume that
most privileges be resolved with user or db level accesses.
@return Access status
@retval FALSE Access granted; But column privileges might need to be
checked.
...
...
@@ -4552,6 +4556,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
Security_context
*
sctx
=
thd
->
security_ctx
;
uint
i
;
ulong
orig_want_access
=
want_access
;
my_bool
locked
=
0
;
GRANT_TABLE
*
grant_table
;
DBUG_ENTER
(
"check_grant"
);
DBUG_ASSERT
(
number
>
0
);
...
...
@@ -4575,11 +4581,9 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
*/
tl
->
grant
.
orig_want_privilege
=
(
want_access
&
~
SHOW_VIEW_ACL
);
}
number
=
i
;
mysql_rwlock_rdlock
(
&
LOCK_grant
);
for
(
tl
=
tables
;
tl
&&
number
--
&&
tl
!=
first_not_own_table
;
tl
=
tl
->
next_global
)
for
(
tl
=
tables
;
number
--
;
tl
=
tl
->
next_global
)
{
sctx
=
test
(
tl
->
security_ctx
)
?
tl
->
security_ctx
:
thd
->
security_ctx
;
...
...
@@ -4632,13 +4636,18 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
}
continue
;
}
GRANT_TABLE
*
grant_table
=
table_hash_search
(
sctx
->
host
,
sctx
->
ip
,
tl
->
get_db_name
(),
sctx
->
priv_user
,
tl
->
get_table_name
(),
FALSE
);
if
(
!
grant_table
)
if
(
!
locked
)
{
locked
=
1
;
mysql_rwlock_rdlock
(
&
LOCK_grant
);
}
if
(
!
(
grant_table
=
table_hash_search
(
sctx
->
host
,
sctx
->
ip
,
tl
->
get_db_name
(),
sctx
->
priv_user
,
tl
->
get_table_name
(),
FALSE
)))
{
want_access
&=
~
tl
->
grant
.
privilege
;
goto
err
;
// No grants
...
...
@@ -4665,11 +4674,13 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
goto
err
;
// impossible
}
}
mysql_rwlock_unlock
(
&
LOCK_grant
);
if
(
locked
)
mysql_rwlock_unlock
(
&
LOCK_grant
);
DBUG_RETURN
(
FALSE
);
err:
mysql_rwlock_unlock
(
&
LOCK_grant
);
if
(
locked
)
mysql_rwlock_unlock
(
&
LOCK_grant
);
if
(
!
no_errors
)
// Not a silent skip of table
{
char
command
[
128
];
...
...
sql/sql_class.cc
View file @
6b3654f9
...
...
@@ -349,29 +349,6 @@ void thd_set_thread_stack(THD *thd, char *stack_start)
thd
->
thread_stack
=
stack_start
;
}
/**
Lock connection data for the set of connections this connection
belongs to
@param thd THD object
*/
void
thd_lock_thread_count
(
THD
*
)
{
mysql_mutex_lock
(
&
LOCK_thread_count
);
}
/**
Lock connection data for the set of connections this connection
belongs to
@param thd THD object
*/
void
thd_unlock_thread_count
(
THD
*
)
{
mysql_cond_broadcast
(
&
COND_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
}
/**
Close the socket used by this connection
...
...
@@ -947,7 +924,14 @@ THD::THD()
protocol_binary
.
init
(
this
);
tablespace_op
=
FALSE
;
tmp
=
sql_rnd_with_mutex
();
/*
Initialize the random generator. We call my_rnd() without a lock as
it's not really critical if two threads modifies the structure at the
same time. We ensure that we have an unique number foreach thread
by adding the address of the stack.
*/
tmp
=
(
ulong
)
(
my_rnd
(
&
sql_rand
)
*
0xffffffff
);
my_rnd_init
(
&
rand
,
tmp
+
(
ulong
)
&
rand
,
tmp
+
(
ulong
)
::
global_query_id
);
substitute_null_with_insert_id
=
FALSE
;
thr_lock_info_init
(
&
lock_info
);
/* safety: will be reset after start */
...
...
@@ -4342,17 +4326,8 @@ void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
{
mysql_mutex_lock
(
&
LOCK_thd_data
);
set_query_inner
(
query_arg
,
query_length_arg
,
cs
);
query_id
=
new_query_id
;
mysql_mutex_unlock
(
&
LOCK_thd_data
);
}
/** Assign a new value to thd->query_id. */
void
THD
::
set_query_id
(
query_id_t
new_query_id
)
{
mysql_mutex_lock
(
&
LOCK_thd_data
);
query_id
=
new_query_id
;
mysql_mutex_unlock
(
&
LOCK_thd_data
);
}
/** Assign a new value to thd->mysys_var. */
...
...
sql/sql_class.h
View file @
6b3654f9
...
...
@@ -3076,7 +3076,10 @@ class THD :public Statement,
{
set_query
(
CSET_STRING
());
}
void
set_query_and_id
(
char
*
query_arg
,
uint32
query_length_arg
,
CHARSET_INFO
*
cs
,
query_id_t
new_query_id
);
void
set_query_id
(
query_id_t
new_query_id
);
void
set_query_id
(
query_id_t
new_query_id
)
{
query_id
=
new_query_id
;
}
void
set_open_tables
(
TABLE
*
open_tables_arg
)
{
mysql_mutex_lock
(
&
LOCK_thd_data
);
...
...
sql/sql_insert.cc
View file @
6b3654f9
...
...
@@ -2038,9 +2038,9 @@ class Delayed_insert :public ilink {
thd
.
unlink
();
// Must be unlinked under lock
my_free
(
thd
.
query
());
thd
.
security_ctx
->
user
=
thd
.
security_ctx
->
host
=
0
;
thread_count
--
;
delayed_insert_threads
--
;
mysql_mutex_unlock
(
&
LOCK_thread_count
);
thread_safe_decrement32
(
&
thread_count
,
&
thread_count_lock
);
mysql_cond_broadcast
(
&
COND_thread_count
);
/* Tell main we are ready */
}
...
...
@@ -2175,9 +2175,9 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
{
if
(
!
(
di
=
new
Delayed_insert
()))
goto
end_create
;
mysql_mutex_lock
(
&
LOCK_thread_count
);
thread_
count
++
;
mysql_mutex_unlock
(
&
LOCK_thread_count
);
thread_
safe_increment32
(
&
thread_count
,
&
thread_count_lock
)
;
/*
Annotating delayed inserts is not supported.
*/
...
...
sql/sql_parse.cc
View file @
6b3654f9
...
...
@@ -645,9 +645,10 @@ void do_handle_bootstrap(THD *thd)
delete
thd
;
#ifndef EMBEDDED_LIBRARY
mysql_mutex_lock
(
&
LOCK_thread_count
);
thread_count
--
;
thread_safe_decrement32
(
&
thread_count
,
&
thread_count_lock
);
in_bootstrap
=
FALSE
;
mysql_mutex_lock
(
&
LOCK_thread_count
);
mysql_cond_broadcast
(
&
COND_thread_count
);
mysql_mutex_unlock
(
&
LOCK_thread_count
);
my_thread_end
();
...
...
@@ -930,9 +931,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd
->
query_plan_flags
=
QPLAN_INIT
;
thd
->
lex
->
sql_command
=
SQLCOM_END
;
/* to avoid confusing VIEW detectors */
thd
->
set_time
();
thd
->
set_query_id
(
get_query_id
());
if
(
!
(
server_command_flags
[
command
]
&
CF_SKIP_QUERY_ID
))
next_query_id
();
thd
->
set_query_id
(
next_query_id
());
else
{
/*
ping, get statistics or similar stateless command.
No reason to increase query id here.
*/
thd
->
set_query_id
(
get_query_id
());
}
inc_thread_running
();
if
(
!
(
server_command_flags
[
command
]
&
CF_SKIP_QUESTIONS
))
...
...
@@ -5021,6 +5029,10 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if
((
db
!=
NULL
)
&&
(
db
!=
any_db
))
{
/*
Check if this is reserved database, like information schema or
performance schema
*/
const
ACL_internal_schema_access
*
access
;
access
=
get_cached_schema_access
(
grant_internal_info
,
db
);
if
(
access
)
...
...
vio/vio.c
View file @
6b3654f9
...
...
@@ -213,9 +213,7 @@ Vio *vio_new(my_socket sd, enum enum_vio_type type, uint flags)
if
((
vio
=
(
Vio
*
)
my_malloc
(
sizeof
(
*
vio
),
MYF
(
MY_WME
))))
{
vio_init
(
vio
,
type
,
sd
,
0
,
flags
);
sprintf
(
vio
->
desc
,
(
vio
->
type
==
VIO_TYPE_SOCKET
?
"socket (%d)"
:
"TCP/IP (%d)"
),
vio
->
sd
);
vio
->
desc
=
(
vio
->
type
==
VIO_TYPE_SOCKET
?
"socket"
:
"TCP/IP"
);
#if !defined(__WIN__)
#if !defined(NO_FCNTL_NONBLOCK)
/*
...
...
@@ -257,7 +255,7 @@ Vio *vio_new_win32pipe(HANDLE hPipe)
if
((
vio
=
(
Vio
*
)
my_malloc
(
sizeof
(
Vio
),
MYF
(
MY_WME
))))
{
vio_init
(
vio
,
VIO_TYPE_NAMEDPIPE
,
0
,
hPipe
,
VIO_LOCALHOST
);
strmov
(
vio
->
desc
,
"named pipe"
)
;
vio
->
desc
=
"named pipe"
;
}
DBUG_RETURN
(
vio
);
}
...
...
@@ -282,7 +280,7 @@ Vio *vio_new_win32shared_memory(HANDLE handle_file_map, HANDLE handle_map,
vio
->
event_conn_closed
=
event_conn_closed
;
vio
->
shared_memory_remain
=
0
;
vio
->
shared_memory_pos
=
handle_map
;
strmov
(
vio
->
desc
,
"shared memory"
)
;
vio
->
desc
=
"shared memory"
;
}
DBUG_RETURN
(
vio
);
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment