Bugs and solutions in sample code for leaders and followers in ACE programmer Guide

Source: Internet
Author: User

The thread pool chapter in the ace programmer guide mentions two modes:

 

Semi-synchronous semi-asynchronous mode and leader and followers mode.

 

A sample program is provided in the book. For convenience, I paste the sample code here:

 

# Include "ACE/config-lite.h"
# If defined (ace_has_threads)

# Include "ACE/OS _ns_string.h"
# Include "ACE/OS _ns_sys_time.h"
# Include "ACE/task. H"
# Include "ACE/containers. H"
# Include "ACE/synch. H"

// Listing 4 Code/ch16
Class follower
{
Public:
Follower (ace_thread_mutex & leader_lock)
: Cond _ (leader_lock)
{
Owner _ = ace_thread: Self ();
}

// Fuzz: Disable check_for_lack_ace_ OS
Int wait (void)
{
Return this-> cond _. Wait ();
}

Int signal (void)
{
Return this-> cond _. Signal ();
}
// Fuzz: Enable check_for_lack_ace_ OS

Ace_thread_t owner (void)
{
Return this-> owner _;
}

PRIVATE:
Ace_condition <ace_thread_mutex> cond _;
Ace_thread_t owner _;
};
// Listing 4
// Listing 1 code/ch16
Class lf_threadpool: Public ace_task <ace_mt_synch>
{
Public:
Lf_threadpool (): shutdown _ (0), current_leader _ (0)
{
Ace_trace (ace_text ("lf_threadpool: TP "));
}

Virtual int SVC (void );

Void shut_down (void)
{
Shutdown _ = 1;
}

PRIVATE:
Int become_leader (void );

Follower * make_follower (void );

Int elect_new_leader (void );

Int leader_active (void)
{
Ace_trace (ace_text ("lf_threadpool: leader_active "));
Return this-> current_leader _! = 0;
}

Void leader_active (ace_thread_t leader)
{
Ace_trace (ace_text ("lf_threadpool: leader_active "));
This-> current_leader _ = Leader;
}

Void process_message (ace_message_block * MB );

Int done (void)
{
Return (shutdown _ = 1 );
}

PRIVATE:
Int shutdown _;
Ace_thread_t current_leader _;
Ace_thread_mutex leader_lock _;
Ace_unbounded_queue <follower *> followers _;
Ace_thread_mutex followers_lock _;
Static long long_time;
};
// Listing 1
// Listing 2 code/ch16
Int
Lf_threadpool: SVC (void)
{
Ace_trace (ace_text ("lf_threadpool: SVC "));
While (! Done ())
{
Become_leader (); // block until this thread is the leader.

Ace_message_block * MB = 0;
Ace_time_value TV (long_time );
TV + = ace_ OS: gettimeofday ();

// Get a message, elect new leader, then process message.
If (this-> getq (MB, & TV) <0)
{
If (elect_new_leader () = 0)
Break;
Continue;
}

Elect_new_leader ();
Process_message (MB );
}

Return 0;
}
// Listing 2
// Listing 3 code/ch16
Int
Lf_threadpool: become_leader (void)
{
Ace_trace (ace_text ("lf_threadpool: become_leader "));

Ace_guard_return
(Ace_thread_mutex, leader_mon, this-> leader_lock _,-1 );
If (leader_active ())
{
Follower * fw = make_follower ();
{
// Wait until told to do so.
While (leader_active ())
FW-> wait ();
}

Delete FW;
}

Ace_debug (lm_debug, ace_text ("(% t) becoming the leader/N ")));

// Mark yourself as the active leader.
Leader_active (ace_thread: Self ());
Return 0;
}

Follower *
Lf_threadpool: make_follower (void)
{
Ace_trace (ace_text ("lf_threadpool: make_follower "));

Ace_guard_return
(Ace_thread_mutex, follower_mon, this-> followers_lock _, 0 );
Follower * Fw;
Ace_new_return (FW, follower (this-> leader_lock _), 0 );
This-> followers _. enqueue_tail (FW );
Return FW;
}
// Listing 3
// Listing 5 code/ch16
Int
Lf_threadpool: elect_new_leader (void)
{
Ace_trace (ace_text ("lf_threadpool: elect_new_leader "));

Ace_guard_return
(Ace_thread_mutex, leader_mon, this-> leader_lock _,-1 );
Leader_active (0 );

// Wake up a follower
If (! Followers _. is_empty ())
{
Ace_guard_return (ace_thread_mutex,
Follower_mon,
This-> followers_lock _,
-1 );
// Get the old follower.
Follower * Fw;
If (this-> followers _. dequeue_head (FW )! = 0)
Return-1;
Ace_debug (lm_debug,
Ace_text ("(% t) resigning and electing % d/N "),
FW-> owner ()));
Return (FW-> signal () = 0 )? 0:-1;
}
Else
{
Ace_debug
(Lm_error, ace_text ("(% t) Oops no followers left/N ")));
Return-1;
}
}
// Listing 5

Void
Lf_threadpool: process_message (ace_message_block * MB)
{
Ace_trace (ace_text ("lf_threadpool: process_message "));
Int msgid;
Ace_ OS: memcpy (& msgid, MB-> rd_ptr (), sizeof (INT ));
MB-> release ();

Ace_debug (lm_debug,
Ace_text ("(% t) started processing message: % d/N "),
Msgid ));
Ace_ OS: Sleep (1 );
Ace_debug (lm_debug,
Ace_text ("(% t) finished processing message: % d/N "),
Msgid ));
}

Long lf_threadpool: long_time = 5l;

Int ace_tmain (INT, ace_tchar * [])
{
Lf_threadpool TP;
TP. Activate (thr_new_lwp | thr_joinable, 5 );

// Wait for a few seconds...
Ace_ OS: Sleep (2 );
Ace_time_value TV (1l );

Ace_message_block * MB;
For (INT I = 0; I <30; I ++)
{
Ace_new_return (MB, ace_message_block (sizeof (INT),-1 );
Ace_ OS: memcpy (MB-> wr_ptr (), & I, sizeof (INT ));
Ace_ OS: Sleep (TV );

// Add a new work item.
TP. putq (MB );
}

Ace_thread_manager: instance ()-> wait ();

Ace_ OS: Sleep (10 );

Return 0;
}

# Else
# Include "ACE/OS _main.h"
# Include "ACE/OS _ns_stdio.h"

Int ace_tmain (INT, ace_tchar * [])
{
Ace_ OS: puts (ace_text ("this example requires threads ."));
Return 0;
}

# Endif/* ace_has_threads */

 

I will briefly explain the idea of the program: first, create a thread pool, which contains five threads, and each thread tries to become a leader,

 

However, there can only be one leader. Therefore, once a thread becomes a leader, other threads can only position themselves as followers,

 

And put the corresponding follower object in a follower queue, and then wait for the opportunity to become a leader, while the follower thread is suspended.

 

The leader thread in the competition tries to capture messages from the message queue. If there is no message in the message queue, the thread will also be suspended.

 

Once a customer puts a message into a queue (equivalent to adding a task), the Leader thread is activated and obtains a message from the message queue,

 

Then, the first thing it does is to find a new leader. The search algorithm is to simply retrieve the followers from the header of the followers queue,

 

Specify it as a new leader and stimulate the followers thread. After completing these tasks, process the messages obtained from the queue and execute the corresponding tasks.

 

After it completes the task processing, it tries to become a leader. if it finds that there are already leaders, it has to position itself as a follower and

 

The corresponding followers are put into the followers queue, waiting for opportunities to become leaders.

 

I tested the above Code on the Windows platform and confirmed that there was a problem. I started to process several messages, and the switching between leaders and followers was still correct,

 

But later, there were several threads that could never become the leader. At last, there were two threads sitting in turn. This was obviously not the expected result.

 

I carefully studied the intermediate process of thread running and found the cause of the problem.

 

The elect_new_leader function does not directly change the current_leader _ variable

The value of the new leader. Instead, the variable is set to 0. Modifying the current_leader _ variable to the value of the new leader is delayed until the new leader thread is activated.

 

Then execute in this thread, so there is a time difference between them, which gives the old leader a chance to steal the leader's position.

 

Assume that the old leader has completed the task. At this time, the thread of the new leader has just been activated by the operating system and has not been able to mark current_leader _

 

His own threadid, the old leader found that there is no one in the leader's position, directly set himself as the leader, and the new leader was originally as the orthodox

 

The selected leader found that there was a leader in the position before setting the leader, so he had to wait again, but because of the elect_new_leader Function

 

The object of its followers has been removed from the queue, so that it never has the opportunity to become a leader, and can only be suspended for a long time.

 

After finding the cause of the problem, modifying the bug is not complicated. You only need to change current_leader _ to the thread ID of the new leader in the elect_new_leader function.

 

The modified code is as follows:

 

# Include "ACE/config-lite.h"
# If defined (ace_has_threads)

# Include "ACE/OS _ns_string.h"
# Include "ACE/OS _ns_sys_time.h"
# Include "ACE/task. H"
# Include "ACE/containers. H"
# Include "ACE/synch. H"
# Include <sstream>

Using namespace STD;

// Listing 4 Code/ch16
Class follower
{
Public:
Follower (ace_thread_mutex & leader_lock)
: Cond _ (leader_lock)
{
Owner _ = ace_thread: Self ();
}

// Fuzz: Disable check_for_lack_ace_ OS
Int wait (void)
{
Return this-> cond _. Wait ();
}

Int signal (void)
{
Return this-> cond _. Signal ();
}
// Fuzz: Enable check_for_lack_ace_ OS

Ace_thread_t owner (void)
{
Return this-> owner _;
}

PRIVATE:
Ace_condition <ace_thread_mutex> cond _;
Ace_thread_t owner _;
};
// Listing 4
// Listing 1 code/ch16
Class lf_threadpool: Public ace_task <ace_mt_synch>
{
Public:
Lf_threadpool (): shutdown _ (0), current_leader _ (0)
{
Ace_trace (ace_text ("lf_threadpool: TP "));
}

Virtual int SVC (void );

Void shut_down (void)
{
Shutdown _ = 1;
}

PRIVATE:
Int become_leader (void );

Follower * make_follower (void );

Int elect_new_leader (void );

Int leader_active (void)
{
Ace_trace (ace_text ("lf_threadpool: leader_active "));
Return this-> current_leader _! = 0;
}

Void leader_active (ace_thread_t leader)
{
Ace_trace (ace_text ("lf_threadpool: leader_active "));
This-> current_leader _ = Leader;
}

Void process_message (ace_message_block * MB );

Int done (void)
{
Return (shutdown _ = 1 );
}

PRIVATE:
Int shutdown _;
Ace_thread_t current_leader _;
Ace_thread_mutex leader_lock _;
Ace_unbounded_queue <follower *> followers _;
Ace_thread_mutex followers_lock _;
Static long long_time;
};
// Listing 1
// Listing 2 code/ch16
Int
Lf_threadpool: SVC (void)
{
Ace_trace (ace_text ("lf_threadpool: SVC "));
While (! Done ())
{
Become_leader (); // block until this thread is the leader.

Ace_message_block * MB = 0;
Ace_time_value TV (long_time );
TV + = ace_ OS: gettimeofday ();

// Get a message, elect new leader, then process message.
If (this-> getq (MB, & TV) <0)
{
Elect_new_leader ();
Break;
}

Elect_new_leader ();
Process_message (MB );
}

Return 0;
}
// Listing 2
// Listing 3 code/ch16
Int
Lf_threadpool: become_leader (void)
{
Ace_trace (ace_text ("lf_threadpool: become_leader "));

Ace_guard_return
(Ace_thread_mutex, leader_mon, this-> leader_lock _,-1 );
If (leader_active ())
{
Follower * fw = make_follower ();
{
// Wait until told to do so.
While (leader_active ())
{
FW-> wait ();
Ace_debug (lm_debug, ace_text ("(% t) thread activate! /N ")));
Break;
}
}

Delete FW;
}

Ace_debug (lm_debug, ace_text ("(% t) becoming the leader/N ")));

// Mark yourself as the active leader.
Leader_active (ace_thread: Self ());
Return 0;
}

Follower *
Lf_threadpool: make_follower (void)
{
Ace_trace (ace_text ("lf_threadpool: make_follower "));

Ace_guard_return
(Ace_thread_mutex, follower_mon, this-> followers_lock _, 0 );
Follower * Fw;
Ace_new_return (FW, follower (this-> leader_lock _), 0 );
This-> followers _. enqueue_tail (FW );
Ace_debug (lm_debug, ace_text ("(% t) thread follower enter queue/N ")));
Return FW;
}
// Listing 3
// Listing 5 code/ch16
Int
Lf_threadpool: elect_new_leader (void)
{
Ace_trace (ace_text ("lf_threadpool: elect_new_leader "));

Ace_guard_return
(Ace_thread_mutex, leader_mon, this-> leader_lock _,-1 );
Leader_active (0 );

// Wake up a follower
If (! Followers _. is_empty ())
{
Ace_guard_return (ace_thread_mutex,
Follower_mon,
This-> followers_lock _,
-1 );
// Get the old follower.
Follower * Fw;
If (this-> followers _. dequeue_head (FW )! = 0)
Return-1;
Leader_active (FW-> owner ());
Ace_debug (lm_debug,
Ace_text ("(% t) resigning and electing % d/N "),
FW-> owner ()));
Return (FW-> signal () = 0 )? 0:-1;
}
Else
{
Ace_debug
(Lm_error, ace_text ("(% t) Oops no followers left/N ")));
Return-1;
}
}
// Listing 5

Void
Lf_threadpool: process_message (ace_message_block * MB)
{
Ace_trace (ace_text ("lf_threadpool: process_message "));
Int msgid;
Ace_ OS: memcpy (& msgid, MB-> rd_ptr (), sizeof (INT ));
MB-> release ();

Ace_debug (lm_debug,
Ace_text ("(% t) started processing message: % d/N "),
Msgid ));
Ace_ OS: Sleep (1 );
Ace_debug (lm_debug,
Ace_text ("(% t) finished processing message: % d/N "),
Msgid ));
}

Long lf_threadpool: long_time = 5l;

Int ace_tmain (INT, ace_tchar * [])
{
Lf_threadpool TP;
TP. Activate (thr_new_lwp | thr_joinable, 5 );

// Wait for a few seconds...
Ace_ OS: Sleep (2 );
Ace_time_value TV (1l );

Ace_message_block * MB;
For (INT I = 0; I <30; I ++)
{
Ace_new_return (MB, ace_message_block (sizeof (INT),-1 );
Ace_ OS: memcpy (MB-> wr_ptr (), & I, sizeof (INT ));
Ace_ OS: Sleep (TV );

// Add a new work item.
TP. putq (MB );
}

Ace_thread_manager: instance ()-> wait ();

Ace_ OS: Sleep (10 );

Return 0;
}

# Else
# Include "ACE/OS _main.h"
# Include "ACE/OS _ns_stdio.h"

Int ace_tmain (INT, ace_tchar * [])
{
Ace_ OS: puts (ace_text ("this example requires threads ."));
Return 0;
}

# Endif/* ace_has_threads */

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.