Freeciv21
Develop your civilization from humble roots to a global empire
taiplayer.c
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2020 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include <fc_config.h>
15 
16 #include <QMutexLocker>
17 
18 /* utility */
19 #include "log.h"
20 
21 /* common */
22 #include "ai.h"
23 #include "city.h"
24 #include "game.h"
25 #include "unit.h"
26 
27 /* server/advisors */
28 #include "infracache.h"
29 
30 /* ai/default */
31 #include "aiplayer.h"
32 
33 /* ai/threaded */
34 #include "taicity.h"
35 
36 #include "taiplayer.h"
37 
38 /* What level of operation we should abort because
39  * of received messages. Lower is more critical;
40  * TAI_ABORT_EXIT means that whole thread should exit,
41  * TAI_ABORT_NONE means that we can continue what we were doing */
46 };
47 
48 static enum tai_abort_msg_class tai_check_messages(struct ai_type *ait);
49 
50 struct tai_thr {
52  struct tai_msgs msgs_to;
53  struct tai_reqs reqs_from;
57 
58 /**********************************************************************/
62 {
63  thrai.thread_running = false;
64 
65  thrai.num_players = 0;
66 }
67 
68 /**********************************************************************/
71 static void tai_thread_start(void *arg)
72 {
73  bool finished = false;
74  struct ai_type *ait = arg;
75 
76  log_debug("New AI thread launched");
77 
78  /* Just wait until we are signaled to shutdown */
79  QMutexLocker locker(&thrai.msgs_to.mutex);
80  while (!finished) {
82 
83  if (tai_check_messages(ait) <= TAI_ABORT_EXIT) {
84  finished = true;
85  }
86  }
87 
88  log_debug("AI thread exiting");
89 }
90 
91 /**********************************************************************/
94 static enum tai_abort_msg_class tai_check_messages(struct ai_type *ait)
95 {
96  enum tai_abort_msg_class ret_abort = TAI_ABORT_NONE;
97 
98  thrai.msgs_to.msglist.lock();
99  while (taimsg_list_size(thrai.msgs_to.msglist) > 0) {
100  struct tai_msg *msg;
101  enum tai_abort_msg_class new_abort = TAI_ABORT_NONE;
102 
103  msg = taimsg_list_get(thrai.msgs_to.msglist, 0);
104  taimsg_list_remove(thrai.msgs_to.msglist, msg);
105  thrai.msgs_to.msglist.unlock();
106 
107  log_debug("Plr thr got %s", taimsgtype_name(msg->type));
108 
109  switch (msg->type) {
110  case TAI_MSG_FIRST_ACTIVITIES:
111  game.server.mutexes.city_list.lock();
112 
114 
115  /* Use _safe iterate in case the main thread
116  * destroyes cities while we are iterating through these. */
117  city_list_iterate_safe(msg->plr->cities, pcity)
118  {
119  tai_city_worker_requests_create(ait, msg->plr, pcity);
120 
121  /* Release mutex for a second in case main thread
122  * wants to do something to city list. */
123  game.server.mutexes.city_list.unlock();
124 
125  /* Recursive message check in case phase is finished. */
126  new_abort = tai_check_messages(ait);
127  game.server.mutexes.city_list.lock();
128  if (new_abort < TAI_ABORT_NONE) {
129  break;
130  }
131  }
133  game.server.mutexes.city_list.unlock();
134 
135  tai_send_req(TAI_REQ_TURN_DONE, msg->plr, NULL);
136 
137  break;
138  case TAI_MSG_PHASE_FINISHED:
139  new_abort = TAI_ABORT_PHASE_END;
140  break;
141  case TAI_MSG_THR_EXIT:
142  new_abort = TAI_ABORT_EXIT;
143  break;
144  default:
145  qCritical("Illegal message type %s (%d) for threaded ai!",
146  taimsgtype_name(msg->type), msg->type);
147  break;
148  }
149 
150  if (new_abort < ret_abort) {
151  ret_abort = new_abort;
152  }
153 
154  delete msg;
155  msg = nullptr;
156 
157  thrai.msgs_to.msglist.lock();
158  }
159  thrai.msgs_to.msglist.unlock();
160 
161  return ret_abort;
162 }
163 
164 /**********************************************************************/
167 void tai_player_alloc(struct ai_type *ait, struct player *pplayer)
168 {
169  struct tai_plr *player_data = new tai_plr{};
170 
171  player_set_ai_data(pplayer, ait, player_data);
172 
173  /* Default AI */
174  dai_data_init(ait, pplayer);
175 }
176 
177 /**********************************************************************/
180 void tai_player_free(struct ai_type *ait, struct player *pplayer)
181 {
182  struct tai_plr *player_data = player_ai_data(pplayer, ait);
183 
184  /* Default AI */
185  dai_data_close(ait, pplayer);
186 
187  if (player_data != NULL) {
188  player_set_ai_data(pplayer, ait, NULL);
189  delete player_data;
190  player_data = nullptr;
191  }
192 }
193 
194 /**********************************************************************/
197 void tai_control_gained(struct ai_type *ait, struct player *pplayer)
198 {
199  thrai.num_players++;
200 
201  log_debug("%s now under threaded AI (%d)", pplayer->name,
203 
204  if (!thrai.thread_running) {
205  thrai.msgs_to.msglist = taimsg_list_new();
206  thrai.reqs_from.reqlist = taireq_list_new();
207 
208  thrai.thread_running = true;
209 
211  thrai.ait->start(QThread::NormalPriority);
212  }
213 }
214 
215 /**********************************************************************/
218 void tai_control_lost(struct ai_type *ait, struct player *pplayer)
219 {
220  thrai.num_players--;
221 
222  log_debug("%s no longer under threaded AI (%d)", pplayer->name,
224 
225  if (thrai.num_players <= 0) {
226  tai_send_msg(TAI_MSG_THR_EXIT, pplayer, NULL);
227 
228  thrai.ait.wait();
229  thrai.thread_running = false;
230 
231  taimsg_list_destroy(thrai.msgs_to.msglist);
232  taireq_list_destroy(thrai.reqs_from.reqlist);
233  }
234 }
235 
236 /**********************************************************************/
239 void tai_refresh(struct ai_type *ait, struct player *pplayer)
240 {
241  if (thrai.thread_running) {
242  thrai.reqs_from.reqlist.lock();
243  while (taireq_list_size(thrai.reqs_from.reqlist) > 0) {
244  struct tai_req *req;
245 
246  req = taireq_list_get(thrai.reqs_from.reqlist, 0);
247  taireq_list_remove(thrai.reqs_from.reqlist, req);
248 
249  thrai.reqs_from.reqlist.unlock();
250 
251  log_debug("Plr thr sent %s", taireqtype_name(req->type));
252 
253  switch (req->type) {
254  case TAI_REQ_WORKER_TASK:
256  break;
257  case TAI_REQ_TURN_DONE:
258  req->plr->ai_phase_done = true;
259  break;
260  }
261 
262  delete req;
263  req = nullptr;
264 
265  thrai.reqs_from.reqlist.lock();
266  }
267  thrai.reqs_from.reqlist.unlock();
268  }
269 }
270 
271 /**********************************************************************/
275 void tai_msg_to_thr(struct tai_msg *msg)
276 {
277  QMutexLocker locker(&thrai.msgs_to.mutex);
278  taimsg_list_append(thrai.msgs_to.msglist, msg);
279  fc_thread_cond_signal(&thrai.msgs_to.thr_cond);
280 }
281 
282 /**********************************************************************/
285 void tai_req_from_thr(struct tai_req *req)
286 {
287  QMutexLocker locker(&thrai.reqs_from.reqlist);
288  taireq_list_append(thrai.reqs_from.reqlist, req);
289 }
290 
291 /**********************************************************************/
void dai_data_init(struct ai_type *ait, struct player *pplayer)
Initialize ai data structure.
Definition: aidata.cpp:50
void dai_data_close(struct ai_type *ait, struct player *pplayer)
Deinitialize ai data structure.
Definition: aidata.cpp:98
#define city_list_iterate_safe(citylist, _city)
Definition: city.h:500
#define city_list_iterate_safe_end
Definition: city.h:521
void set_func(void(tfunc)(void *), void *tdata)
Definition: fcthread.cpp:20
struct civ_game game
Definition: game.cpp:47
void initialize_infrastructure_cache(struct player *pplayer)
Do all tile improvement calculations and cache them for later.
Definition: infracache.cpp:245
#define log_debug(message,...)
Definition: log.h:65
void * player_ai_data(const struct player *pplayer, const struct ai_type *ai)
Return pointer to ai data of given player and ai type.
Definition: player.cpp:1838
void player_set_ai_data(struct player *pplayer, const struct ai_type *ai, void *data)
Attach ai data to player.
Definition: player.cpp:1846
Definition: ai.h:42
struct civ_game::@28::@32 server
Definition: player.h:231
struct city_list * cities
Definition: player.h:263
char name[MAX_LEN_NAME]
Definition: player.h:233
bool ai_phase_done
Definition: player.h:246
Definition: taimsg.h:31
struct player * plr
Definition: taimsg.h:33
enum taimsgtype type
Definition: taimsg.h:32
QMutex mutex
Definition: taiplayer.h:31
struct taimsg_list * msglist
Definition: taiplayer.h:32
QWaitCondition thr_cond
Definition: taiplayer.h:30
Definition: taimsg.h:37
struct player * plr
Definition: taimsg.h:39
enum taireqtype type
Definition: taimsg.h:38
struct taireq_list * reqlist
Definition: taiplayer.h:36
bool thread_running
Definition: taiplayer.c:54
int num_players
Definition: taiplayer.c:51
fcThread ait
Definition: taiplayer.c:55
struct tai_msgs msgs_to
Definition: taiplayer.c:52
struct tai_reqs reqs_from
Definition: taiplayer.c:53
void tai_city_worker_requests_create(struct ai_type *ait, struct player *pplayer, struct city *pcity)
Create worker request for the city.
Definition: taicity.c:59
void tai_req_worker_task_rcv(struct tai_req *req)
Receive message from thread to main thread.
Definition: taicity.c:490
void tai_send_req(enum taireqtype type, struct player *pplayer, void *data)
Construct and send request from player thread.
Definition: taimsg.c:45
void tai_send_msg(enum taimsgtype type, struct player *pplayer, void *data)
Construct and send message to player thread.
Definition: taimsg.c:24
void tai_msg_to_thr(struct tai_msg *msg)
Send message to thread.
Definition: taiplayer.c:275
bool tai_thread_running(void)
Return whether player thread is running.
Definition: taiplayer.c:294
void tai_control_gained(struct ai_type *ait, struct player *pplayer)
We actually control the player.
Definition: taiplayer.c:197
void tai_control_lost(struct ai_type *ait, struct player *pplayer)
We no longer control the player.
Definition: taiplayer.c:218
void tai_player_alloc(struct ai_type *ait, struct player *pplayer)
Initialize player for use with threaded AI.
Definition: taiplayer.c:167
tai_abort_msg_class
Definition: taiplayer.c:42
@ TAI_ABORT_NONE
Definition: taiplayer.c:45
@ TAI_ABORT_PHASE_END
Definition: taiplayer.c:44
@ TAI_ABORT_EXIT
Definition: taiplayer.c:43
void tai_player_free(struct ai_type *ait, struct player *pplayer)
Free player from use with threaded AI.
Definition: taiplayer.c:180
void tai_init_threading(void)
Initialize ai thread.
Definition: taiplayer.c:61
void tai_req_from_thr(struct tai_req *req)
Thread sends message.
Definition: taiplayer.c:285
void tai_refresh(struct ai_type *ait, struct player *pplayer)
Check for messages sent by player thread.
Definition: taiplayer.c:239
static enum tai_abort_msg_class tai_check_messages(struct ai_type *ait)
Handle messages from message queue.
Definition: taiplayer.c:94
static void tai_thread_start(void *arg)
This is main function of ai thread.
Definition: taiplayer.c:71
struct tai_thr thrai