@@ -2530,7 +2530,8 @@ int TC_LOG_MMAP::open(const char *opt_name)
25302530 inited=2 ;
25312531
25322532 npages=(uint)file_length/tc_log_page_size;
2533- DBUG_ASSERT (npages >= 3 ); // to guarantee non-empty pool
2533+ if (npages < 3 ) // to guarantee non-empty pool
2534+ goto err;
25342535 if (!(pages=(PAGE *)my_malloc (npages*sizeof (PAGE), MYF (MY_WME|MY_ZEROFILL))))
25352536 goto err;
25362537 inited=3 ;
@@ -2541,9 +2542,9 @@ int TC_LOG_MMAP::open(const char *opt_name)
25412542 pg->state =PS_POOL;
25422543 mysql_mutex_init (key_PAGE_lock, &pg->lock , MY_MUTEX_INIT_FAST);
25432544 mysql_cond_init (key_PAGE_cond, &pg->cond , 0 );
2544- pg->start =(my_xid *)(data + i*tc_log_page_size);
2545- pg->end =(my_xid *)(pg->start + tc_log_page_size);
2545+ pg->ptr = pg->start =(my_xid *)(data + i*tc_log_page_size);
25462546 pg->size =pg->free =tc_log_page_size/sizeof (my_xid);
2547+ pg->end = pg->start + pg->size ;
25472548 }
25482549 pages[0 ].size =pages[0 ].free =
25492550 (tc_log_page_size-TC_LOG_HEADER_SIZE)/sizeof (my_xid);
@@ -2569,8 +2570,9 @@ int TC_LOG_MMAP::open(const char *opt_name)
25692570
25702571 syncing= 0 ;
25712572 active=pages;
2573+ DBUG_ASSERT (npages >= 2 );
25722574 pool=pages+1 ;
2573- pool_last= pages+npages-1 ;
2575+ pool_last_ptr= &(( pages+npages-1 )-> next ) ;
25742576
25752577 return 0 ;
25762578
@@ -2579,6 +2581,16 @@ int TC_LOG_MMAP::open(const char *opt_name)
25792581 return 1 ;
25802582}
25812583
2584+ /* *
2585+ Get the total amount of potentially usable slots for XIDs in TC log.
2586+ */
2587+
2588+ uint TC_LOG_MMAP::size () const
2589+ {
2590+ return (tc_log_page_size-TC_LOG_HEADER_SIZE)/sizeof (my_xid) +
2591+ (npages - 1 ) * (tc_log_page_size/sizeof (my_xid));
2592+ }
2593+
25822594/* *
25832595 there is no active page, let's got one from the pool.
25842596
@@ -2596,13 +2608,11 @@ void TC_LOG_MMAP::get_active_from_pool()
25962608 PAGE **p, **best_p=0 ;
25972609 int best_free;
25982610
2599- if (syncing)
2600- mysql_mutex_lock (&LOCK_pool);
2601-
2611+ mysql_mutex_lock (&LOCK_pool);
26022612 do
26032613 {
26042614 best_p= p= &pool;
2605- if ((*p)->waiters == 0 ) // can the first page be used ?
2615+ if ((*p)->waiters == 0 && (*p)-> free > 0 ) // can the first page be used ?
26062616 break ; // yes - take it.
26072617
26082618 best_free=0 ; // no - trying second strategy
@@ -2617,20 +2627,21 @@ void TC_LOG_MMAP::get_active_from_pool()
26172627 }
26182628 while ((*best_p == 0 || best_free == 0 ) && overflow ());
26192629
2630+ mysql_mutex_assert_owner (&LOCK_active);
26202631 active=*best_p;
2632+
2633+ /* Unlink the page from the pool. */
2634+ if (!(*best_p)->next )
2635+ pool_last_ptr= best_p;
2636+ *best_p=(*best_p)->next ;
2637+ mysql_mutex_unlock (&LOCK_pool);
2638+
2639+ mysql_mutex_lock (&active->lock );
26212640 if (active->free == active->size ) // we've chosen an empty page
26222641 {
2623- tc_log_cur_pages_used++ ;
2642+ thread_safe_increment ( tc_log_cur_pages_used, &LOCK_status) ;
26242643 set_if_bigger (tc_log_max_pages_used, tc_log_cur_pages_used);
26252644 }
2626-
2627- if ((*best_p)->next ) // unlink the page from the pool
2628- *best_p=(*best_p)->next ;
2629- else
2630- pool_last=*best_p;
2631-
2632- if (syncing)
2633- mysql_mutex_unlock (&LOCK_pool);
26342645}
26352646
26362647/* *
@@ -2663,7 +2674,7 @@ TC_LOG::enum_result TC_LOG_MMAP::commit(THD *thd, bool all)
26632674 my_xid xid= thd->transaction .xid_state .xid .get_my_xid ();
26642675
26652676 if (all && xid)
2666- if ((cookie= log_xid (thd, xid)))
2677+ if (! (cookie= log_xid (thd, xid)))
26672678 DBUG_RETURN (RESULT_ABORTED); // Failed to log the transaction
26682679
26692680 if (ha_commit_low (thd, all))
@@ -2725,9 +2736,17 @@ int TC_LOG_MMAP::log_xid(THD *thd, my_xid xid)
27252736 /* no active page ? take one from the pool */
27262737 if (active == 0 )
27272738 get_active_from_pool ();
2739+ else
2740+ mysql_mutex_lock (&active->lock );
27282741
27292742 p=active;
2730- mysql_mutex_lock (&p->lock );
2743+
2744+ /*
2745+ p->free is always > 0 here because to decrease it one needs
2746+ to take p->lock and before it one needs to take LOCK_active.
2747+ But checked that active->free > 0 under LOCK_active and
2748+ haven't release it ever since
2749+ */
27312750
27322751 /* searching for an empty slot */
27332752 while (*p->ptr )
@@ -2742,37 +2761,49 @@ int TC_LOG_MMAP::log_xid(THD *thd, my_xid xid)
27422761 p->free --;
27432762 p->state = PS_DIRTY;
27442763
2745- /* to sync or not to sync - this is the question */
2746- mysql_mutex_unlock (&LOCK_active);
2747- mysql_mutex_lock (&LOCK_sync);
27482764 mysql_mutex_unlock (&p->lock );
2749-
2765+ mysql_mutex_lock (&LOCK_sync);
27502766 if (syncing)
27512767 { // somebody's syncing. let's wait
2768+ mysql_mutex_unlock (&LOCK_active);
2769+ mysql_mutex_lock (&p->lock );
27522770 p->waiters ++;
27532771 /*
27542772 note - it must be while (), not do ... while () here
27552773 as p->state may be not PS_DIRTY when we come here
27562774 */
27572775 while (p->state == PS_DIRTY && syncing)
2776+ {
2777+ mysql_mutex_unlock (&p->lock );
27582778 mysql_cond_wait (&p->cond , &LOCK_sync);
2779+ mysql_mutex_lock (&p->lock );
2780+ }
27592781 p->waiters --;
27602782 err= p->state == PS_ERROR;
27612783 if (p->state != PS_DIRTY) // page was synced
27622784 {
2785+ mysql_mutex_unlock (&LOCK_sync);
27632786 if (p->waiters == 0 )
27642787 mysql_cond_signal (&COND_pool); // in case somebody's waiting
2765- mysql_mutex_unlock (&LOCK_sync );
2788+ mysql_mutex_unlock (&p-> lock );
27662789 goto done; // we're done
27672790 }
2768- } // page was not synced! do it now
2769- DBUG_ASSERT (active == p && syncing == 0 );
2770- mysql_mutex_lock (&LOCK_active);
2771- syncing=p; // place is vacant - take it
2791+ DBUG_ASSERT (!syncing);
2792+ mysql_mutex_unlock (&p->lock );
2793+ syncing = p;
2794+ mysql_mutex_unlock (&LOCK_sync);
2795+
2796+ mysql_mutex_lock (&LOCK_active);
2797+ }
2798+ else
2799+ {
2800+ syncing= p; // place is vacant - take it
2801+ mysql_mutex_unlock (&LOCK_sync);
2802+ }
2803+
27722804 active=0 ; // page is not active anymore
27732805 mysql_cond_broadcast (&COND_active); // in case somebody's waiting
27742806 mysql_mutex_unlock (&LOCK_active);
2775- mysql_mutex_unlock (&LOCK_sync);
27762807 err= sync ();
27772808
27782809done:
@@ -2789,22 +2820,32 @@ int TC_LOG_MMAP::sync()
27892820 sit down and relax - this can take a while...
27902821 note - no locks are held at this point
27912822 */
2792- err= my_msync (fd, syncing->start , 1 , MS_SYNC);
2823+ err= my_msync (fd, syncing->start , syncing-> size * sizeof (my_xid) , MS_SYNC);
27932824
27942825 /* page is synced. let's move it to the pool */
27952826 mysql_mutex_lock (&LOCK_pool);
2796- pool_last-> next = syncing;
2797- pool_last= syncing;
2827+ (*pool_last_ptr)= syncing;
2828+ pool_last_ptr= &( syncing-> next ) ;
27982829 syncing->next =0 ;
27992830 syncing->state = err ? PS_ERROR : PS_POOL;
2800- mysql_cond_broadcast (&syncing->cond ); // signal "sync done"
28012831 mysql_cond_signal (&COND_pool); // in case somebody's waiting
28022832 mysql_mutex_unlock (&LOCK_pool);
28032833
28042834 /* marking 'syncing' slot free */
28052835 mysql_mutex_lock (&LOCK_sync);
2836+ mysql_cond_broadcast (&syncing->cond ); // signal "sync done"
28062837 syncing=0 ;
2807- mysql_cond_signal (&active->cond ); // wake up a new syncer
2838+
2839+ /*
2840+ we check the "active" pointer without LOCK_active. Still, it's safe -
2841+ "active" can change from NULL to not NULL any time, but it
2842+ will take LOCK_sync before waiting on active->cond. That is, it can never
2843+ miss a signal.
2844+ And "active" can change to NULL only by the syncing thread
2845+ (the thread that will send a signal below)
2846+ */
2847+ if (active)
2848+ mysql_cond_signal (&active->cond ); // wake up a new syncer
28082849 mysql_mutex_unlock (&LOCK_sync);
28092850 return err;
28102851}
@@ -2821,9 +2862,9 @@ int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
28212862
28222863 DBUG_ASSERT (*x == xid);
28232864 DBUG_ASSERT (x >= p->start && x < p->end );
2824- *x=0 ;
28252865
28262866 mysql_mutex_lock (&p->lock );
2867+ *x=0 ;
28272868 p->free ++;
28282869 DBUG_ASSERT (p->free <= p->size );
28292870 set_if_smaller (p->ptr , x);
0 commit comments