char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
947fd9bb2
char/aliceai-t5-moe
1#pragma once 2 3#include "server-task.h" 4 5#include <condition_variable> 6#include <deque> 7#include <exception> 8#include <mutex> 9#include <thread> 10#include <vector> 11#include <unordered_set> 12 13// struct for managing server tasks 14// in most cases, use server_response_reader to post new tasks and retrieve results 15struct server_queue { 16private : 17int id = 0 ; 18bool running = false; 19bool sleeping = false; 20bool req_stop_sleeping = false; 21int64_t time_last_task = 0 ; 22 23// queues 24std ::deque < server_task > queue_tasks ; 25std ::deque < server_task > queue_tasks_deferred ; 26// tasks declined while yielding, put back in queue_tasks once the yield is done 27// note: kept as a member so that cleanup_pending_task() can also reach them 28std ::deque < server_task > queue_tasks_unhandled ; 29 30std ::mutex mutex_tasks ; 31std ::condition_variable condition_tasks ; 32 33// used by yield_to_queue, all fields are guarded by mutex_tasks 34struct worker_t { 35std ::thread thread ; 36std ::condition_variable cv ;// the worker sleeps on this until a yield starts 37std ::exception_ptr exception ;// exception thrown while processing tasks, if any 38bool stop = false; 39bool busy = false;// set by yield_to_queue(), cleared by the worker once it is done processing tasks 40bool yielding = false;// work() is still running on the start_loop() thread 41 }; 42worker_t worker ; 43 44// callback functions 45std ::function < bool (server_task && ,bool )> callback_new_task ; 46std ::function < void (void )> callback_update_slots ; 47std ::vector < std ::function < void (bool )>>callback_sleeping_state ; 48 49public : 50~ server_queue () { worker_stop (); } 51 52// Add a new task to the end of the queue 53int post ( server_task && task , bool front = false); 54 55// multi-task version of post() 56int post ( std :: vector < server_task > && tasks , bool front = false); 57 58// Add a new task, but defer until one slot is available 59void defer ( server_task && task ); 60 61// Get the next id for creating a new task 62int get_new_id (); 63 64// Call when the state of one slot is changed, it will move one task from deferred to main queue 65// prioritize tasks that use the specified slot (otherwise, pop the first deferred task) 66void pop_deferred_task ( int id_slot ); 67 68// if sleeping, request exiting sleep state and wait until it is done 69// returns immediately if not sleeping 70void wait_until_no_sleep (); 71 72bool is_sleeping () { 73std :: unique_lock < std :: mutex > lock ( mutex_tasks ); 74return sleeping ; 75} 76 77// end the start_loop routine 78void terminate (); 79 80/** 81* Main loop consists of these steps: 82* - Wait until a new task arrives 83* - Process the task (i.e. maybe copy data into slot) 84* - Check if multitask is finished 85* - Update all slots 86* 87* Sleeping procedure (disabled if idle_sleep_ms < 0): 88* - If there is no task after idle_sleep_ms, enter sleeping state 89* note: metrics tasks are processed as usual, but do not reset the idle timer 90* - Call callback_sleeping_state(true) 91* - Wait until req_stop_sleeping is set to true 92* - Call callback_sleeping_state(false) 93* - Exit sleeping state 94*/ 95void start_loop ( int64_t idle_sleep_ms = -1 ); 96 97// while waiting for work() to finish, run process_new_tasks on the worker thread 98// returns once work() is done (may throw exceptions) 99// must be called from start_loop() thread (ideally inside callback_update_slots) 100// use case: return metrics while encode/decode is running 101// ref: https://github.com/ggml-org/llama.cpp/pull/27041 102// 103// tasks declined by callback_new_task are put back in the queue once this returns 104void yield_to_queue ( std :: function < void () > && work ); 105 106// for metrics 107size_t queue_tasks_deferred_size () { 108std :: unique_lock < std :: mutex > lock ( mutex_tasks ); 109return queue_tasks_deferred . size (); 110} 111 112// 113// Functions below are not thread-safe, must only be used before start_loop() is called 114// 115 116// Register function to process a new task 117// the second argument tells whether the queue is currently yielding (see yield_to_queue) 118// only then may the callback return false to decline the task, and it must leave it 119// untouched, so that it can be put back in the queue later 120// note: while yielding, the callback runs on worker thread, not main thread 121void on_new_task ( std :: function < bool ( server_task && , bool ) > callback ) { 122callback_new_task = std :: move ( callback ); 123} 124 125// Register the function to be called when all slots data is ready to be processed 126void on_update_slots ( std :: function < void ( void ) > callback ) { 127callback_update_slots = std :: move ( callback ); 128} 129 130// Register callback for sleeping state change; multiple callbacks are allowed 131// for example: register order cb0, cb1, cb2 132// entering sleep: queue.sleeping = true --> cb0(true) --> cb1(true) --> cb2(true) 133// leaving sleep: cb2(false) --> cb1(false) --> cb0(false) --> queue.sleeping = false 134// note: caller will hold mutex_tasks while calling the callbacks 135void on_sleeping_state ( std :: function < void ( bool ) > callback ) { 136callback_sleeping_state . push_back ( std :: move ( callback )); 137} 138 139private : 140void cleanup_pending_task ( int id_target ); 141 142// process all pending tasks in the queue 143// returns true if the queue is terminated, false if there is no more task to process 144// while yielding, declined tasks are moved to queue_tasks_unhandled 145bool process_new_tasks ( bool is_yielding ); 146 147// for worker_t 148void worker_loop (); 149void worker_stop (); 150}; 151 152// struct for managing server responses 153// in most cases, use server_response_reader to retrieve results 154struct server_response { 155private : 156bool running = true; 157 158// for keeping track of all tasks waiting for the result 159std ::unordered_set < int > waiting_task_ids; 160 161// the main result queue (using ptr for polymorphism) 162std ::vector < server_task_result_ptr > queue_results; 163 164std ::mutex mutex_results; 165std ::condition_variable condition_results; 166 167public : 168// add the id_task to the list of tasks waiting for response 169void add_waiting_task_id( int id_task ); 170 171void add_waiting_task_ids( const std :: unordered_set < int > & id_tasks ); 172 173// when the request is finished, we can remove task associated with it 174void remove_waiting_task_id( int id_task ); 175 176// remove multiple tasks from waiting list 177void remove_waiting_task_ids( const std :: unordered_set < int > & id_tasks ); 178 179// This function blocks the thread until there is a response for one of the id_tasks 180server_task_result_ptr recv( const std :: unordered_set < int > & id_tasks ); 181 182// same as recv(), but have timeout in seconds 183// if timeout is reached, nullptr is returned 184server_task_result_ptr recv_with_timeout( const std :: unordered_set < int > & id_tasks , int timeout ); 185 186// single-task version of recv() 187server_task_result_ptr recv( int id_task ); 188 189// Send a new result to a waiting id_task 190void send( server_task_result_ptr && result ); 191 192// broadcast a new result to all waiting tasks 193// (used by router mode) 194void broadcast( server_task_result_ptr && result ); 195 196// terminate the waiting loop 197void terminate(); 198}; 199 200// RAII wrapper to make working with server_queue and server_response easier 201// it provides a generator-like API for server responses 202// support pooling connection state and aggregating multiple results 203struct server_response_reader { 204std ::unordered_set < int > id_tasks; 205server_queue & queue_tasks; 206server_response & queue_results; 207size_t received_count = 0 ; 208bool cancelled = false; 209int polling_interval_seconds; 210 211// tracking generation state and partial tool calls 212// only used by streaming completions 213std ::vector < task_result_state > states; 214 215// should_stop function will be called each polling_interval_seconds 216server_response_reader ( server_queue & queue_tasks , server_response & queue_results , int polling_interval_seconds ) 217: queue_tasks( queue_tasks ), queue_results( queue_results ), polling_interval_seconds( polling_interval_seconds ) {} 218~server_response_reader() { 219stop(); 220} 221 222int get_new_id () { 223return queue_tasks . get_new_id (); 224} 225 226// if front = true, the task will be posted to the front of the queue (high priority) 227void post_task ( server_task && task , bool front = false); 228void post_tasks ( std :: vector < server_task > && tasks , bool front = false); 229bool has_next () const ; 230 231// return nullptr if should_stop() is true before receiving a result 232// note: if one error is received, it will stop further processing and return error result 233server_task_result_ptr next ( const std :: function < bool () > & should_stop ); 234 235struct batch_response { 236bool is_terminated = false; // if true, indicates that processing was stopped before all results were received 237std ::vector < server_task_result_ptr > results; 238server_task_result_ptr error; // nullptr if no error 239} ; 240// aggregate multiple results 241batch_response wait_for_all ( const std ::function < bool () > & should_stop); 242 243void stop (); 244};