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
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
mariadb
Commits
73c3a77b
Commit
73c3a77b
authored
Jul 01, 2010
by
Sergei Golubchik
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bug lp:578117 - Wrong usage of mutex LOCK_sync and LOCK_active in XA
redone locking in TC_LOG_MMAP::log_xid
parent
c4636b9d
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
77 additions
and
60 deletions
+77
-60
mysql-test/suite/pbxt/r/pbxt_xa.result
mysql-test/suite/pbxt/r/pbxt_xa.result
+0
-1
mysql-test/suite/pbxt/t/pbxt_xa.test
mysql-test/suite/pbxt/t/pbxt_xa.test
+0
-5
sql/log.cc
sql/log.cc
+77
-54
No files found.
mysql-test/suite/pbxt/r/pbxt_xa.result
View file @
73c3a77b
drop table if exists t1, t2;
CALL mtr.add_suppression("Found wrong usage of mutex 'LOCK_sync' and 'LOCK_active'");
CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=innodb;
CREATE TABLE t2 (b INT PRIMARY KEY) ENGINE=pbxt;
BEGIN;
...
...
mysql-test/suite/pbxt/t/pbxt_xa.test
View file @
73c3a77b
...
...
@@ -4,11 +4,6 @@
drop
table
if
exists
t1
,
t2
;
--
enable_warnings
# This warning is indication of a real bug, MBug#578117.
# But it is not a regression, so we suppress it to get a clean test run.
# This suppression must be removed as part of MBug#578117 fix.
CALL
mtr
.
add_suppression
(
"Found wrong usage of mutex 'LOCK_sync' and 'LOCK_active'"
);
#
# bug lp:544173, xa crash with two 2pc-capable storage engines without binlog
#
...
...
sql/log.cc
View file @
73c3a77b
...
...
@@ -5285,39 +5285,39 @@ void sql_print_information(const char *format, ...)
/********* transaction coordinator log for 2pc - mmap() based solution *******/
/*
the log consists of a file, m
mapped to a
memory.
file is divided
on
pages of tc_log_page_size size.
(usable size of the first page is smaller because of log header)
there
's
PAGE control structure for each page
each page (or rather
PAGE control structure) can be in one of three
states - active, syncing, pool.
there could be only one page in
active or syncing states
,
but many in pool - pool is fifo queue.
usual lifecycle of a page is pool->active->syncing->pool
"active" page -
is a page where new xid's are logged.
the page stays active as long as syncing slot is taken.
"syncing" page is being synced to disk. no new xid can be added to it.
when the sync is done the page is moved to a pool and an active page
the log consists of a file, m
apped to
memory.
file is divided
into
pages of tc_log_page_size size.
(usable size of the first page is smaller because of
the
log header)
there
is a
PAGE control structure for each page
each page (or rather
its PAGE control structure) can be in one of
the three
states - active, syncing, pool.
there could be only one page in
the active or syncing state
,
but many in pool - pool is
a
fifo queue.
the usual lifecycle of a page is pool->active->syncing->pool.
the "active" page
is a page where new xid's are logged.
the page stays active as long as
the
syncing slot is taken.
the
"syncing" page is being synced to disk. no new xid can be added to it.
when the sync
ing
is done the page is moved to a pool and an active page
becomes "syncing".
the result of such an architecture is a natural "commit grouping" -
If commits are coming faster than the system can sync, they do not
stall. Instead, all commit that came since the last sync are
logged to the same page, and they all are synced with the next -
stall. Instead, all commit
s
that came since the last sync are
logged to the same
"active"
page, and they all are synced with the next -
one - sync. Thus, thought individual commits are delayed, throughput
is not decreasing.
when a xid is added to an active page, the thread of this xid waits
when a
n
xid is added to an active page, the thread of this xid waits
for a page's condition until the page is synced. when syncing slot
becomes vacant one of these waiters is awaken to take care of syncing.
it syncs the page and signals all waiters that the page is synced.
PAGE::waiters is used to count these waiters, and a page may never
become active again until waiters==0 (that is all waiters from the
previous sync have noticed the sync was completed)
previous sync have noticed th
at th
e sync was completed)
note, that the page becomes "dirty" and has to be synced only when a
new xid is added into it. Removing a xid from a page does not make it
dirty - we don't sync removals to disk.
dirty - we don't sync
xid
removals to disk.
*/
ulong
tc_log_page_waits
=
0
;
...
...
@@ -5383,7 +5383,8 @@ int TC_LOG_MMAP::open(const char *opt_name)
inited
=
2
;
npages
=
(
uint
)
file_length
/
tc_log_page_size
;
DBUG_ASSERT
(
npages
>=
3
);
// to guarantee non-empty pool
if
(
npages
<
3
)
// to guarantee non-empty pool
goto
err
;
if
(
!
(
pages
=
(
PAGE
*
)
my_malloc
(
npages
*
sizeof
(
PAGE
),
MYF
(
MY_WME
|
MY_ZEROFILL
))))
goto
err
;
inited
=
3
;
...
...
@@ -5440,7 +5441,7 @@ err:
-# if there're waiters - take the one with the most free space.
@todo
TODO
page merging. try to allocate adjacent page first,
page merging. try to allocate adjacent page first,
so that they can be flushed both in one sync
*/
...
...
@@ -5449,7 +5450,6 @@ void TC_LOG_MMAP::get_active_from_pool()
PAGE
**
p
,
**
best_p
=
0
;
int
best_free
;
if
(
syncing
)
pthread_mutex_lock
(
&
LOCK_pool
);
do
...
...
@@ -5470,20 +5470,21 @@ void TC_LOG_MMAP::get_active_from_pool()
}
while
((
*
best_p
==
0
||
best_free
==
0
)
&&
overflow
());
safe_mutex_assert_owner
(
&
LOCK_active
);
active
=*
best_p
;
if
(
active
->
free
==
active
->
size
)
// we've chosen an empty page
{
tc_log_cur_pages_used
++
;
set_if_bigger
(
tc_log_max_pages_used
,
tc_log_cur_pages_used
);
}
if
((
*
best_p
)
->
next
)
// unlink the page from the pool
*
best_p
=
(
*
best_p
)
->
next
;
else
pool_last
=*
best_p
;
if
(
syncing
)
pthread_mutex_unlock
(
&
LOCK_pool
);
pthread_mutex_lock
(
&
active
->
lock
);
if
(
active
->
free
==
active
->
size
)
// we've chosen an empty page
{
tc_log_cur_pages_used
++
;
set_if_bigger
(
tc_log_max_pages_used
,
tc_log_cur_pages_used
);
}
}
/**
...
...
@@ -5538,7 +5539,7 @@ int TC_LOG_MMAP::log_xid(THD *thd, my_xid xid)
pthread_mutex_lock
(
&
LOCK_active
);
/*
if active page is full - just wait...
if
the
active page is full - just wait...
frankly speaking, active->free here accessed outside of mutex
protection, but it's safe, because it only means we may miss an
unlog() for the active page, and we're not waiting for it here -
...
...
@@ -5550,9 +5551,17 @@ int TC_LOG_MMAP::log_xid(THD *thd, my_xid xid)
/* no active page ? take one from the pool */
if
(
active
==
0
)
get_active_from_pool
();
else
pthread_mutex_lock
(
&
active
->
lock
);
p
=
active
;
pthread_mutex_lock
(
&
p
->
lock
);
/*
p->free is always > 0 here because to decrease it one needs
to take p->lock and before it one needs to take LOCK_active.
But checked that active->free > 0 under LOCK_active and
haven't release it ever since
*/
/* searching for an empty slot */
while
(
*
p
->
ptr
)
...
...
@@ -5566,38 +5575,51 @@ int TC_LOG_MMAP::log_xid(THD *thd, my_xid xid)
*
p
->
ptr
++=
xid
;
p
->
free
--
;
p
->
state
=
DIRTY
;
/* to sync or not to sync - this is the question */
pthread_mutex_unlock
(
&
LOCK_active
);
pthread_mutex_lock
(
&
LOCK_sync
);
pthread_mutex_unlock
(
&
p
->
lock
);
pthread_mutex_lock
(
&
LOCK_sync
);
if
(
syncing
)
{
// somebody's syncing. let's wait
pthread_mutex_unlock
(
&
LOCK_active
);
pthread_mutex_lock
(
&
p
->
lock
);
p
->
waiters
++
;
/*
note - it must be while (), not do ... while () here
as p->state may be not DIRTY when we come here
*/
while
(
p
->
state
==
DIRTY
&&
syncing
)
for
(;;)
{
int
not_dirty
=
p
->
state
!=
DIRTY
;
pthread_mutex_unlock
(
&
p
->
lock
);
if
(
not_dirty
||
!
syncing
)
break
;
pthread_cond_wait
(
&
p
->
cond
,
&
LOCK_sync
);
pthread_mutex_lock
(
&
p
->
lock
);
}
p
->
waiters
--
;
err
=
p
->
state
==
ERROR
;
if
(
p
->
state
!=
DIRTY
)
// page was synced
{
pthread_mutex_unlock
(
&
LOCK_sync
);
if
(
p
->
waiters
==
0
)
pthread_cond_signal
(
&
COND_pool
);
// in case somebody's waiting
pthread_mutex_unlock
(
&
LOCK_sync
);
pthread_mutex_unlock
(
&
p
->
lock
);
goto
done
;
// we're done
}
}
// page was not synced! do it now
DBUG_ASSERT
(
active
==
p
&&
syncing
==
0
);
DBUG_ASSERT
(
!
syncing
);
pthread_mutex_unlock
(
&
p
->
lock
);
syncing
=
p
;
pthread_mutex_unlock
(
&
LOCK_sync
);
pthread_mutex_lock
(
&
LOCK_active
);
syncing
=
p
;
// place is vacant - take it
active
=
0
;
// page is not active anymore
pthread_cond_broadcast
(
&
COND_active
);
// in case somebody's waiting
pthread_cond_broadcast
(
&
COND_active
);
pthread_mutex_unlock
(
&
LOCK_active
);
}
else
{
syncing
=
p
;
// place is vacant - take it
pthread_mutex_unlock
(
&
LOCK_sync
);
active
=
0
;
// page is not active anymore
pthread_cond_broadcast
(
&
COND_active
);
pthread_mutex_unlock
(
&
LOCK_active
);
}
err
=
sync
();
done:
...
...
@@ -5614,7 +5636,7 @@ int TC_LOG_MMAP::sync()
sit down and relax - this can take a while...
note - no locks are held at this point
*/
err
=
my_msync
(
fd
,
syncing
->
start
,
1
,
MS_SYNC
);
err
=
my_msync
(
fd
,
syncing
->
start
,
syncing
->
size
*
sizeof
(
my_xid
)
,
MS_SYNC
);
/* page is synced. let's move it to the pool */
pthread_mutex_lock
(
&
LOCK_pool
);
...
...
@@ -5622,19 +5644,20 @@ int TC_LOG_MMAP::sync()
pool_last
=
syncing
;
syncing
->
next
=
0
;
syncing
->
state
=
err
?
ERROR
:
POOL
;
pthread_cond_broadcast
(
&
syncing
->
cond
);
// signal "sync done"
pthread_cond_signal
(
&
COND_pool
);
// in case somebody's waiting
pthread_mutex_unlock
(
&
LOCK_pool
);
/* marking 'syncing' slot free */
pthread_mutex_lock
(
&
LOCK_sync
);
pthread_cond_broadcast
(
&
syncing
->
cond
);
// signal "sync done"
syncing
=
0
;
/*
we check the "active" pointer without LOCK_active. Still, it's safe -
"active" can change from NULL to not NULL any time, but it
will take LOCK_sync before waiting on active->cond. That is, it can never
miss a signal.
And "active" can change to NULL only after LOCK_sync, so this is safe too.
And "active" can change to NULL only by the syncing thread
(the thread that will send a signal below)
*/
if
(
active
)
pthread_cond_signal
(
&
active
->
cond
);
// wake up a new syncer
...
...
@@ -5654,9 +5677,9 @@ void TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
DBUG_ASSERT
(
*
x
==
xid
);
DBUG_ASSERT
(
x
>=
p
->
start
&&
x
<
p
->
end
);
*
x
=
0
;
pthread_mutex_lock
(
&
p
->
lock
);
*
x
=
0
;
p
->
free
++
;
DBUG_ASSERT
(
p
->
free
<=
p
->
size
);
set_if_smaller
(
p
->
ptr
,
x
);
...
...
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